package org.example.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.example.config.ApiConfig;
import org.example.entity.*;
import org.example.service.BaseInfoService;
import org.example.service.QueryDataTask;
import org.example.thread.ThreadPoolManager;
import org.example.util.*;
import org.example.util.constantUtil.ConstantUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static org.example.entity.Fields.*;
import static org.example.entity.Fields.INSERT;
import static org.example.entity.Fields.MAINTENANCE;
import static org.example.entity.Fields.STORAGE;
import static org.example.enums.ControlCode.LOCK;
import static org.example.enums.OnOffStatus.*;
import static org.example.enums.ResultCode.FAIL;
import static org.example.enums.ResultCode.SUCCESS;

@Service
@Slf4j
public class BaseInfoServiceImpl implements BaseInfoService {

    @Autowired
    private ApiConfig apiConfig;
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private TimeseriesService timeseriesService;

    private ExecutorService executorService= ThreadPoolManager.executorService;


    // thingsboard访问设备uri前缀
    private static final String LIMIT = ConstantUtil.LIMIT;// 最大返回数量限制
    private static final long QUERY_TIME_LIMIT = 30; // 查询时间上线，默认最近30天
    private static final long ONE_DAY = 86400000;

    // 无效数据
    private static final String INVALID_DATA=ConstantUtil.INVALID_DATA;

    //3.2接口获取用户设备使用，同DeviceUtil
    private static final Integer PAGE_SIZE=1000;
    private static final String TYPE="电池包";



    private static int judgeOnOffStatus(String lifeTs){
        long oneMin=60000;
        long twoMin=120000;
        long fiveHour=5*60*oneMin;
        long oneDay=86400000;
        Calendar calendar = Calendar.getInstance();
        long nowTs=calendar.getTimeInMillis();
        long deviceTs=Long.parseLong(lifeTs);
        if((nowTs-deviceTs)<=twoMin){// 2分钟以内，在线
            return ONLINE.onOffStatus;
        }
        else if((nowTs-deviceTs)<=fiveHour){// 5小时以内，休眠
            return SLEEP.onOffStatus;
        }
        else {// 5小时以外，离线
            return OFFLINE.onOffStatus;
        }

    }
    private static Double getKmDistance(Double distance){
        BigDecimal distanceBig=BigDecimal.valueOf(distance);
        BigDecimal thousand=BigDecimal.valueOf(1000);
        return distanceBig.divide(thousand).doubleValue();
    }
    private static int[] hexToBinary(String hexString, int numBits) {
        int[] binaryArray = new int[numBits];
        String binaryString = new BigInteger(hexString, 16).toString(2);
        int binaryStringLength = binaryString.length();
        int numLeadingZeros = numBits - binaryStringLength;
        for (int i = 0; i < numLeadingZeros; i++) {
            binaryArray[i] = 0;
        }
        for (int i = numLeadingZeros; i < numBits; i++) {
            binaryArray[i] = binaryString.charAt(i - numLeadingZeros) - '0';
        }
        return binaryArray;
    }

    /**
     * 3.1查询电池包基本信息
     *
     * @param imei
     * @return
     */
    public Result getBaseInfo(String imei, String token) {
        BatteryBaseInfo batteryBaseInfo = new BatteryBaseInfo();
        //1.调用httpclient工具类的各种接口获取到需要的基本信息
        //先从映射表根据SN码获取UUID
//        String uuid=DeviceUtil.SNtoUUID.get(imei);
        String uuid = deviceService.getUUIDByImei(imei);
        log.info("imei:" + imei + " DeviceID:" + uuid);
        Map<String,Object> result=timeseriesService.getTimeseries(uuid,token);
        if(result.get("message")!=null){
            return new Result(FAIL.code, false, null,(String)result.get("message"));
        }
        //在线离线状态--------------------------------------------还未添加
//        String lifeTs=(String)result.get("Life_ts");
//        if(lifeTs!=null){
//            batteryBaseInfo.setOnOffStatus(judgeOnOffStatus(lifeTs));
//        }
        batteryBaseInfo.setOnOffStatus(OnOffStatusService.getOnOffStatus(result,imei));
        //电池包编号
        batteryBaseInfo.setPackNo((String)result.get("PACK_SN"));
        batteryBaseInfo.setSn((String)result.get("SN_4G"));//4G SN
        // IMEI编号
        batteryBaseInfo.setImei(imei);
        //告警数量 alarmNum
        int alarmNum=0;
        String firstError=(String)result.get("firstError");
        String secondError=(String)result.get("secondError");
        if(firstError!=null && !firstError.equals(INVALID_DATA)){

            int[] alarm = hexToBinary(firstError, 32);
            for(int i=0;i<alarm.length;i++){
                if(alarm[i]==1){
                    alarmNum++;
                    log.info("firstError bit: "+(alarm.length-1-i));
                }
            }
        }
        if(secondError!=null && !secondError.equals(INVALID_DATA)){

            int[] alarm = hexToBinary(secondError, 32);
            for(int i=0;i<alarm.length;i++){
                if(alarm[i]==1){
                    alarmNum++;
                    log.info("secondError bit: "+(alarm.length-1-i));
                }
            }
        }
        batteryBaseInfo.setAlarmNum(alarmNum);
        //电池类型
        if(result.get("battery_type")!=null){
            switch (Integer.parseInt((String) result.get("battery_type"))) {
                case 1://铅酸电池
                    batteryBaseInfo.setBatteryType("Lead-acid battery");
                    break;
                case 2://镍锌电池
                    batteryBaseInfo.setBatteryType("NiMH battery");
                    break;
                case 3://磷酸铁锂电池
                    batteryBaseInfo.setBatteryType("Lithium iron phosphate battery");
                    break;
                case 4://锰酸锂电池
                    batteryBaseInfo.setBatteryType("Lithium manganate battery");
                    break;
                case 5://钴酸锂电池
                    batteryBaseInfo.setBatteryType("Lithium cobalt oxide battery");
                    break;
                case 6://三元电池
                    batteryBaseInfo.setBatteryType("Ternary battery");
                    break;
                case 7://聚合物电池
                    batteryBaseInfo.setBatteryType("Polymer batteries");
                    break;
                case 8://钛酸锂电池
                    batteryBaseInfo.setBatteryType("Lithium titanate battery");
                    break;
                default:
                    log.warn("batteryType:", (String) result.get("batteryType"));
                    batteryBaseInfo.setBatteryType("undefined");
                    break;
            }
        }
        //BMS编号
        batteryBaseInfo.setBmsNo((String)result.get("SN_BMS"));
        //电芯个数
        if(result.get("racket_num")!=null){
            batteryBaseInfo.setElectricNum(Integer.parseInt((String) result.get("racket_num")));
        }
        //循环次数cycleNum
        if(result.get("cycleNum")!=null){
            batteryBaseInfo.setCycleNum((Integer) result.get("cycleNum"));
        }
        //当前总电压
        if(result.get("rackTotalVoltage")!=null){
            batteryBaseInfo.setRackTotalVoltage(Double.parseDouble((String)result.get("rackTotalVoltage")));
        }
        //单体最高电压
        if(result.get("maxCellVoltage")!=null){
            batteryBaseInfo.setMaxCellVoltage((Integer)result.get("maxCellVoltage"));
        }
        //单体最高电压对应序号
        if(result.get("maxCellVoltageCellid")!=null){
            batteryBaseInfo.setMaxCellVoltageCellid((Integer)result.get("maxCellVoltageCellid"));
        }
        //单体最低电压
        if(result.get("minCellVoltage")!=null){
            batteryBaseInfo.setMinCellVoltage((Integer)result.get("minCellVoltage"));
        }
        //单体最低电压对应序号
        if(result.get("minCellVoltageCellid")!=null){
            batteryBaseInfo.setMinCellVoltageCellid((Integer)result.get("minCellVoltageCellid"));
        }
        //电压最后采集时间yyyy-MM-dd HH:mm:ss
        if(result.get("rackTotalVoltage_ts")!=null){
            long ts = Long.parseLong((String)result.get("rackTotalVoltage_ts"));
            batteryBaseInfo.setVoltageCollectTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(ts));
        }
        //经度
        batteryBaseInfo.setLng((String)result.get("longitude"));
        if(result.get("longitude_ts")!=null){
            long Longitude_ts = Long.parseLong((String)result.get("longitude_ts"));
            batteryBaseInfo.setGpsCollectTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Longitude_ts));
        }
        //纬度
        batteryBaseInfo.setLat((String)result.get("latitude"));
        if(result.get("latitude_ts")!=null){
            long Latitude_ts = Long.parseLong((String)result.get("latitude_ts"));
            batteryBaseInfo.setGpsCollectTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Latitude_ts));
        }
        //里程distance
        Long distance=(Long) result.get("distance");//单位KM
        if(distance==null){
            batteryBaseInfo.setDistance(0.0);
        }else {
//            batteryBaseInfo.setDistance(getKmDistance(Double.parseDouble(distance)));
            batteryBaseInfo.setDistance(Double.valueOf(distance));
        }
        //充放电状态
        if(result.get("Battery_Status")!=null){
            int chargeStatus = Integer.parseInt((String)result.get("Battery_Status"));
            System.out.println("BatteryStatus:"+chargeStatus);
            batteryBaseInfo.setChargeStatus(ChargeStatusUtil.decode(chargeStatus));
        }

        //充电总电量
        if(result.get("electricChargeSum")!=null){
            batteryBaseInfo.setElectricChargeSum(Double.valueOf((String)result.get("electricChargeSum")));
        }
        //BMS批次号
        batteryBaseInfo.setBmsBatchNo((String) result.get("BMS_pcbBatch"));



        // 今日里程 distanceToday
        Long currentTimestamps=System.currentTimeMillis();//当前时间戳
        Long oneDayTimestamps= Long.valueOf(60*60*24*1000);
        Long zeroTimestamps=currentTimestamps-(currentTimestamps+60*60*8*1000)%oneDayTimestamps;//当日零点时间戳

        String hisUrl=apiConfig.getDataApiPrefix()+uuid+apiConfig.getDataApiSuffix() +
                "?keys=distance&startTs="+zeroTimestamps+"&endTs="+currentTimestamps;
        System.out.println(hisUrl);
        //查询从零点到当前时间的distance和电池状态
        String[] resultStrHis=HttpClientUtil.get_data(hisUrl,token);//String 格式获取结果, [0]为statusCode，[1]为结果(结果可能不是json)
        if(Integer.valueOf(resultStrHis[0])!=200){//没有正确拿到信息
            log.error("Cannot get distanceHis "+"code:"+resultStrHis[0]+" result:"+resultStrHis[1]);
        }
        else{//正确拿到结果，可能为空
            JSONObject jsonResultHis = JSONObject.parseObject(resultStrHis[1]);
            //distance
            try{
                JSONArray distanceArray=jsonResultHis.getJSONArray("distance");
//            System.out.println(distanceArray.size());
                List<Long> distanceTs=new LinkedList<>();//用于按时间排序
                Map<Long,Long> ts_distance=new HashMap<>();//用于找对应时间的distance
                Double distanceToday=0.0;//初始化里程
                for(int i=0;i<distanceArray.size();i++){//将ts和value存进去
                    distanceTs.add(Long.parseLong(distanceArray.getJSONObject(i).get("ts").toString()));
                    ts_distance.put(Long.parseLong(distanceArray.getJSONObject(i).get("ts").toString()),Long.parseLong(TimeseriesService.cutPre(distanceArray.getJSONObject(i).get("value").toString()),16));
                }
                distanceTs.sort(Comparator.naturalOrder());//升序排序
                //最早时间里程
                Double distanceStart=Double.valueOf(ts_distance.get(distanceTs.get(0)));
                System.out.println("startTs:"+distanceTs.get(0));
                //最晚时间里程
                Double distanceEnd=Double.valueOf(ts_distance.get(distanceTs.get(distanceTs.size()-1)));
                System.out.println("endTs:"+distanceTs.get(distanceTs.size()-1));
                distanceToday=distanceEnd-distanceStart;
//                log.info("distanceStart:",distanceStart," distanceEnd:",distanceEnd," distanceToday:", distanceToday);
                System.out.println("distanceStart:"+distanceStart+" distanceEnd:"+distanceEnd+" distanceToday:"+ distanceToday);
                batteryBaseInfo.setDistanceToday(getKmDistance(distanceToday));
            }
            catch (NullPointerException e){//这段时间内没有上传distance，即为空的情况
                log.warn("There's no distance today",e);
                batteryBaseInfo.setDistanceToday(0.0);
            }
        }

        // DI(电池在柜)检测和bms即将休眠标志
        String inputSwitch=(String) result.get("inputSwitch");
        if(inputSwitch!=null){
            if(inputSwitch.equals(INVALID_DATA)){ // 无效数据
                batteryBaseInfo.setDi(Integer.valueOf(INVALID_DATA));
                batteryBaseInfo.setBmsSleep(Integer.valueOf(INVALID_DATA));
            }else {
                int[] bmsSwitchCode = hexToBinary(inputSwitch, 8);// 0到len-1分别为bit7到bit0
                System.out.println("inputSwitch:"+result.get("inputSwitch"));
                log.info("inputSwitch:"+result.get("inputSwitch"));
                // bit3 - bmsSwitchCode[4]DI检测, bit4 - bmsSwitch[3]BMS即将休眠
                batteryBaseInfo.setDi(bmsSwitchCode[4]);
                batteryBaseInfo.setBmsSleep(bmsSwitchCode[3]);
            }

        }





        System.out.println("SN:"+batteryBaseInfo.getSn()+"imei:"+batteryBaseInfo.getImei());
        return new Result(SUCCESS.code,true,batteryBaseInfo, "success");
    }

    /**
     * 3.2 查询电池包信息
     *
     * @param imei
     * @param onOffStatus
     * @param current
     * @param size
     * @return
     */

    public Result getBatteryList(String imei,Integer onOffStatus, Integer current, Integer size,String token){
        //根据packNo模糊获取所有的
        BatteryList batteryList=new BatteryList();
        String msg="success";
        ///api/customer/{customerId}/devices{?page,pageSize,sortOrder,sortProperty,textSearch,type}

        /**
         * 1. /api/auth/user获取当前用户，通过此处判断token是否过期或无效
         */
        String getCurrentUserUrl=apiConfig.getCurrentUserApi();
        String[] resultStr = HttpClientUtil.get_data(getCurrentUserUrl, token);
        if (Integer.valueOf(resultStr[0]) != 200) {//没有正确拿到信息
            log.error("3.2 /api/auth/user code:" + resultStr[0] + " result:" + resultStr[1]);
            try {//错误但是返回json格式
                //尝试拿数据
                if(resultStr[1]==null){
                    return new Result(FAIL.code, false,null,"result[1] is null!");
                }
                JSONObject jsonResult = JSONObject.parseObject(resultStr[1]);
                // 错误信息不为null但是没有message项
                if(!jsonResult.containsKey("message")){
                    return new Result(FAIL.code, false,null,resultStr[1]);
                }
                // 有message项
                return new Result(FAIL.code, false, null,jsonResult.get("message").toString());

            } catch (JSONException e) {//错误而且返回的不是JSON格式
//                System.out.println("resultStr: "+resultStr);
                return new Result(FAIL.code, false,null,resultStr[1]);
            }
        }
        /**
         * 2. 是否为租户管理员，来获取设备imei列表
         */
        JSONObject jsonResult = JSONObject.parseObject(resultStr[1]);
        String authority=ConstantUtil.USER_TENANT_ADMIN;
        if(jsonResult.get("authority")==null){
            authority=ConstantUtil.USER_TENANT_ADMIN;
        } else {
            authority=jsonResult.get("authority").toString();
        }
        log.info("3.2 /api/auth/user Authority:{}",authority);

        Map<String,String> devices=new HashMap<>();
        if(authority.equals(ConstantUtil.USER_TENANT_ADMIN)){ // 1.租户管理员
            log.info("Tenant Admin requesting...");
            devices.putAll(DeviceUtil.SNtoUUID);  // 设备列表是总表，这里不能直接赋值（浅拷贝），否则会造成对原列表的修改，应该复制一份
        } else if(authority.equals(ConstantUtil.USER_CUSTOMER)){ // 2.客户
            //查询客户id，/api/customer/{customerId}/deviceInfos获取该客户的所有设备，从而得到设备imei列表
            log.info("Customer requesting:{}",authority);
            Map<String,String> customerDevices=new HashMap<>();
            // a.查询客户的id
            if(jsonResult.getJSONObject("customerId").get("id")==null){
                return new Result(FAIL.code, false, null,"Cannot get customerId!");
            }
            String customerId=jsonResult.getJSONObject("customerId").get("id").toString();
            // b.从第一页开始获取该客户的设备列表
            int page=0;
            String customerDeviceUrl=apiConfig.getCustomerDeviceInfosApiPrefix()+customerId+apiConfig.getCustomerDeviceInfosApiSuffix()
                    +"?"+"pageSize="+PAGE_SIZE+"&"+"page="+page+"&"+"type="+TYPE;
            String[] customerDeviceResultStr=HttpClientUtil.get_data(customerDeviceUrl,token);//String 格式获取结果, [0]为statusCode，[1]为结果(结果可能不是json)
            if(customerDeviceResultStr==null){
                return new Result(FAIL.code, false, null,"Cannot get customer devices!");
            }
            if(Integer.parseInt(customerDeviceResultStr[0])!=200){//没有正确拿到信息
                log.error("GetDeviceERROR: code:"+customerDeviceResultStr[0]+" result:"+customerDeviceResultStr[1]+"url:"+customerDeviceUrl);

                try{//错误但是返回json格式
                    //尝试拿数据
                    JSONObject CustomerDeviceJsonResult = JSONObject.parseObject(customerDeviceResultStr[1]);
                    log.info(jsonResult.toString());
                    return new Result(FAIL.code, false, null,CustomerDeviceJsonResult.get("message").toString());
                } catch (Exception e){//错误而且返回的不是JSON格式
                    return new Result(FAIL.code, false, null,customerDeviceResultStr[1]);
                }
            }
            int totalPages=0;
            while(Integer.parseInt(customerDeviceResultStr[0])==200){
                System.out.println("page"+page);
                totalPages++;
                //获取设备列表
                JSONObject customerDeviceJsonResult = JSONObject.parseObject(customerDeviceResultStr[1]);
                JSONArray dataList=customerDeviceJsonResult.getJSONArray("data");
                //遍历提取id和name存到map里
                log.info("Customer Device num:"+dataList.size());
                for(int i=0;i<dataList.size();i++){
                    customerDevices.put(dataList.getJSONObject(i).get("name").toString(),
                            dataList.getJSONObject(i).getJSONObject("id").get("id").toString());
                }
                //判断是否有还有下一页
                if(!Boolean.parseBoolean(customerDeviceJsonResult.get("hasNext").toString())){//最后一页
                    System.out.println("最后一页啦，page:"+page+" totalPages:"+totalPages);
                    break;
                }
                page++;//下一页继续
                customerDeviceUrl=apiConfig.getCustomerDeviceInfosApiPrefix()+customerId+apiConfig.getCustomerDeviceInfosApiSuffix()
                        +"?"+"pageSize="+PAGE_SIZE+"&"+"page="+page+"&"+"type="+TYPE;
                customerDeviceResultStr=HttpClientUtil.get_data(customerDeviceUrl,token);//String 格式获取结果, [0]为statusCode，[1]为结果(结果可能不是json)
                if(customerDeviceResultStr==null){
                    log.error("Cannot get customer devices! url:"+customerDeviceUrl);
                    break;
                }
            }
            devices.putAll(customerDevices);  // 设备列表为客户设备列表

        } else { // 3.其他不知道什么东西
            log.info("Something else requesting...");
            devices.putAll(DeviceUtil.SNtoUUID);  // 设备列表暂定为总表，如你所见，我也不知道会有什么东西
        }
        // =============================到此处获得的devices是所有当前用户的设备，如果是管理员就是所有设备=============================
        /**
         * 3. 对获取到的设备列表进行imei模糊筛选得到符合条件的完整列表，模糊匹配后拿到一个map
         */
        //先根据imei筛选设备 map为一次筛选，按传入的imei进行模糊匹配
//        Map<String, String> map=parseMapForFilter(DeviceUtil.SNtoUUID,imei); // 原来的，无法区分客户
        Map<String, String> map=parseMapForFilter(devices,imei);  //根据是租户管理员还是客户设备列表不同
        batteryList.setCurrent(current);
        batteryList.setSize(size);
        // 没有符合用户输入imei条件的设备
        // =================================到此处获得的map是用户imei模糊匹配后的所有设备=====================================
        if(map==null){//说明SNtoUUID是null
            batteryList.setRecords(null);
            batteryList.setTotal(0);
            batteryList.setPages(0);
        }

        else {
            List<Object> list=new ArrayList<>();// 用于保存当前页的设备信息

            /**
             * 使用多线程进行筛选在线状态（筛锤子筛，所有设备都查一遍几千台怎么筛）
             */
            // ====================这段压根没有启用====================
            // 传入了onOffStatus的情况下要先筛选
            if(onOffStatus!=null){
//                // 创建线程池
//                int corePoolSize=Runtime.getRuntime().availableProcessors(); // 获取核心线程数，使用机器处理器核心数量
//                ExecutorService executorService = Executors.newFixedThreadPool(2*corePoolSize);  // 线程池大小设置为核心线程数的两倍
                // 创建一个列表来保存所有任务的结果。
                List<Future<Map<String,Object>>> futures = new ArrayList<>();
                for(String key:map.keySet()){// 这个key是imei
                    String uuid=map.get(key);
                    Future<Map<String,Object>> future = executorService.submit(new QueryDataTask(uuid, token));  // 提交任务查询数据
                    futures.add(future);  // 结果添加到列表
                }
                for (Future<Map<String,Object>> future : futures) {
                    try {
                        Map<String,Object> result = future.get(); // 阻塞等待任务执行完成并获取结果

                        /**
                         * 处理代码，判断设备在线状态，将在线设备添加到二次筛选map
                         */

                        if(result.get("message")!=null){ // 数据查询失败
                            log.warn((String)result.get("message"));
                            continue;
                        }
                        String imeiInfo=(String)result.get("imei");
                        if(imei==null){ // 查询不到imei
                            continue;
                        }
                        // 获取在线离线状态(全部状态)
                        Integer onOffStatusResult=OnOffStatusService.getOnOffStatus(result,imeiInfo);
                        System.out.println(" onOffStatus:"+onOffStatusResult);
                        // 无法获取到在线状态或在线状态不匹配，将其移除
                        if(onOffStatusResult==null||!onOffStatusResult.equals(onOffStatus)){
                            map.remove(imeiInfo);
                        }
                    } catch (InterruptedException | ExecutionException e) {
                        // 处理异常
                        log.error("MultiThread Exception! ",e);
                    }
                }
            }
            // ====================这段压根没有启用====================
            // Total为两次筛选后的map大小
            int total=map.size();
            batteryList.setTotal(total);  // 元素总个数
            /**
             * 4.对模糊匹配后的map进行分页，并拿到当前页的所有设备进行查询
             */
            // a.总页数
            int pages=(int)Math.ceil(total*1.0/size);
            if(pages!=0){
                if(current<1||current>pages){
                    return new Result(FAIL.code,false,null, "current out of pages");
                }
            }
            batteryList.setPages(pages);// 总页数

            //再从筛选出的设备里查询，只查询current页的size个
            int start = (current - 1) * size;
            int end = Math.min(start + size, total);
            for (int i = start; i < end; i++) {
                String key = map.keySet().toArray()[i].toString();
                // 调用基础信息接口批量查询设备
                Result rs=getBaseInfo(key,token);
                list.add(rs.getData());
            }
            batteryList.setRecords(list);

        }

        log.info("onOffStatus requested:"+onOffStatus);
        return new Result(SUCCESS.code,true,batteryList, msg);
    }
    public  Map<String, String> parseMapForFilter(Map<String, String > map, String filters) {
        if (map == null) {
            return null;
        } else {
            if(filters==null||filters.isEmpty()){
                return map;
            }
            map = map.entrySet().stream()
                    .filter((e) -> checkKey(e.getKey(),filters))
                    .collect(Collectors.toMap(
                            (e) -> (String) e.getKey(),
                            (e) -> e.getValue()
                    ));
        }
        return map;
    }
    private boolean checkKey(String key,String filters) {
        if (key.indexOf(filters)>-1){
            return true;
        }else {
            return false;
        }
    }

    /**
     * 3.4 电池包实时状态信息
     *
     * @param imei
     * @return
     */
    public Result getStatusInfo(String imei, String token) {
        //1.调用httpclient工具类的各种接口获取到需要的基本信息
        String msg = "success";//返回的信息，默认success
        BatteryStatusInfo batteryStatusInfo = new BatteryStatusInfo();
        //先从映射表根据imei码获取UUID
//        String uuid=DeviceUtil.SNtoUUID.get(imei);
        String uuid = deviceService.getUUIDByImei(imei);
        log.info("imei:"+imei+" DeviceID:"+uuid);
        Map<String,Object> result=timeseriesService.getTimeseries(uuid,token);
        if(result.get("message")!=null){
            return new Result(FAIL.code, false, null,(String) result.get("message"));
        }
        //在线离线状态 onOffStatus
//        String lifeTs=(String)result.get("Life_ts");
//        if(lifeTs!=null){
//            batteryStatusInfo.setOnOffStatus(judgeOnOffStatus(lifeTs));
//        }
        batteryStatusInfo.setOnOffStatus(OnOffStatusService.getOnOffStatus(result,imei));
        //packSN
        batteryStatusInfo.setPackNo((String)result.get("PACK_SN"));
        //充放电状态
        if(result.get("Battery_Status")!=null){
            int chargeStatus = Integer.parseInt((String)result.get("Battery_Status"));
            batteryStatusInfo.setChargeStatus(ChargeStatusUtil.decode(chargeStatus));
        }
        //电压最后采集时间yyyy-MM-dd HH:mm:ss
        if(result.get("rackTotalVoltage_ts")!=null){
            long ts = Long.parseLong((String)result.get("rackTotalVoltage_ts"));
            batteryStatusInfo.setCollectTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(ts));
        }
        //当前总电压
        if(result.get("rackTotalVoltage")!=null){
            batteryStatusInfo.setRackTotalVoltage(Double.parseDouble((String) result.get("rackTotalVoltage")));
        }
        //单体最高电压
        if(result.get("maxCellVoltage")!=null){
            batteryStatusInfo.setRackMaxCellVoltage((Integer)result.get("maxCellVoltage"));
        }
        //单体最高电压对应序号
        if(result.get("maxCellVoltageCellid")!=null){
            batteryStatusInfo.setRackMaxVolCellID((Integer)result.get("maxCellVoltageCellid"));
        }
        //单体最低电压
        if(result.get("minCellVoltage")!=null){
            batteryStatusInfo.setRackMinCellVoltage((Integer)result.get("minCellVoltage"));
        }
        //单体最低电压对应序号
        if(result.get("minCellVoltageCellid")!=null){
            batteryStatusInfo.setRackMinVolCellID((Integer)result.get("minCellVoltageCellid"));
        }
        //单体平均电压
        if(result.get("rackAverageVoltage")!=null){
            batteryStatusInfo.setRackAverageVoltage(Double.parseDouble((String) result.get("rackAverageVoltage")));
        }
        //当前电量 rackSOC
        if(result.get("racketSOC")!=null){
            batteryStatusInfo.setRackSOC(Double.valueOf((Integer)result.get("racketSOC")));
        }
        //电池健康值 rackSOH
        if(result.get("racketSOH")!=null){
            batteryStatusInfo.setRackSOH(Double.valueOf((Integer)result.get("racketSOH")));
        }
        //电芯最高温度
        if(result.get("maxTemp")!=null){
            batteryStatusInfo.setCellHightTemp(Double.valueOf((Integer)result.get("maxTemp")));//有40的偏移量
        }
        //电芯最低温度
        if(result.get("minTemp")!=null){
            batteryStatusInfo.setCellLowTemp(Double.valueOf((Integer)result.get("minTemp")));//有40的偏移量
        }
        // 软件版本号
        String softBms=(String) result.get("SOFT_BMS");
        if(softBms!=null && softBms.length()>=2){
            // 获取前两位转为十进制
            String softBmsBig=softBms.substring(0,2);
            // 判断是否符合十六进制
            String regex="^[A-Fa-f0-9]+$";
            System.out.println("softBMS:"+softBms+" bms:"+String.valueOf(Integer.parseInt(softBmsBig,16)));
            if(!softBmsBig.matches((regex))){
                log.warn("softBms not hex! softBms:{} big:{}",softBms,softBmsBig);
            } else{
                batteryStatusInfo.setBms(String.valueOf(Integer.parseInt(softBmsBig,16)));//02中的软件版本号
                batteryStatusInfo.setBmscat(String.valueOf(Integer.parseInt(softBmsBig,16)));//同上
            }


        }

        // sn，实际是imei
        batteryStatusInfo.setSn(imei);
        // 位置N23.123456,E132.123456,N是纬度latitude，E是经度Longitude
        batteryStatusInfo.setLoc("N"+result.get("latitude")+","+"E"+result.get("longitude"));

        // 环境温度envTemp和tempEnv B1 22环境温度第一个
        List<Integer> envTemps=(List<Integer>)result.get("envTemps");
        if(envTemps!=null){
            batteryStatusInfo.setTempEnv(envTemps.get(0));
            batteryStatusInfo.setEnvTemp(envTemps.get(0));
        }
        // 板卡温度boardTemp B1 20电芯温度第一个
        //电芯温度1 tempC1 使用B1数据包中序号为20的第一个电芯温度
        //电芯温度2 tempC2 使用B1数据包中序号为20的第一个电芯温度
        List<Integer> cellTemps=(List<Integer>)result.get("celltemps");
        if(cellTemps!=null){
            batteryStatusInfo.setBoardTemp(cellTemps.get(0));
            batteryStatusInfo.setTempC1(cellTemps.get(0));
            if(cellTemps.size()>1){
                batteryStatusInfo.setTempC2(cellTemps.get(1));
            }
        }
        //所有单体电压
        if(result.get("vols")!=null){
            Map<String,Integer>cellVols=(Map<String, Integer>)result.get("vols");
            List<Integer> cellVolList = new LinkedList<>(cellVols.values());
            batteryStatusInfo.setCellList(cellVolList);
        }
        //MOS管温度tempMos 使用B1数据包中序号为24的第一个MOS温度
        List<Integer> mosTemps=(List<Integer>)result.get("mosTemps");
        if(mosTemps!=null){
            batteryStatusInfo.setTempMos(mosTemps.get(0));
        }

        String firstError=(String) result.get("firstError");
        System.out.println(firstError);
        if(firstError!=null && !firstError.equals(INVALID_DATA)){
            int[] alarm = hexToBinary(firstError, 32);//注意是从高位到低位存储
//            int[] alarm =DecodeUtil.hexToBinary(firstError,17);
            // bit i 对应下标为alarm.length - 1 - i=32-1-i=31-i
            //BMS温度过高 4 11 13 16对应27 20 18 15
//            batteryStatusInfo.setBmsTempH(alarm[4]|alarm[11]|alarm[13]|alarm[16]);
            batteryStatusInfo.setBmsTempH(alarm[27]|alarm[20]|alarm[18]|alarm[15]);
            //电芯温度过低 5 12 14对应26 19 17
//            batteryStatusInfo.setCellTempL(alarm[5]|alarm[12]|alarm[14]);
            batteryStatusInfo.setCellTempL(alarm[26]|alarm[19]|alarm[17]);
            //电芯温度过高 4 11 13对应27 20 18
//            batteryStatusInfo.setCellTempH(alarm[4]|alarm[11]|alarm[13]);
            batteryStatusInfo.setCellTempH(alarm[27]|alarm[20]|alarm[18]);
            //电芯欠压 2对应29
//            batteryStatusInfo.setCellUv(alarm[2]);
            batteryStatusInfo.setCellUv(alarm[29]);
            //电芯过压 3对应28
//            batteryStatusInfo.setCellOv(alarm[3]);
            batteryStatusInfo.setCellOv(alarm[28]);
            //放电过流-bit6放电过流告警 6对应25
//            batteryStatusInfo.setDisChargeOc(alarm[6]);
            batteryStatusInfo.setDisChargeOc(alarm[25]);
            //短路-bit15短路告警 15对应16
//            batteryStatusInfo.setSc(alarm[15]);
            batteryStatusInfo.setSc(alarm[16]);
            //充电过流-bit7充电过流告警 7对应24
//            batteryStatusInfo.setChargeOc(alarm[7]);
            batteryStatusInfo.setChargeOc(alarm[24]);
        }
        //均衡开关-------------------------默认1
        batteryStatusInfo.setBalance(1);


        // 实际上只要心跳包接口没问题返回code=200就算success，数据有问题再说
        return new Result(SUCCESS.code, true, batteryStatusInfo, "success");
    }

    /**
     * 3.5电池包下所有单体电芯电压
     *
     * @param imei
     * @return
     */
    public Result getCellVols(String imei,String token){
        //1.调用httpclient工具类的各种接口获取到需要的基本信息
        String msg="success";//返回的信息，默认success
        CellVol cellVol=new CellVol();
        //先从映射表根据imei码获取UUID
//        String uuid=DeviceUtil.SNtoUUID.get(imei);
        String uuid = deviceService.getUUIDByImei(imei);
        log.info("imei:"+imei+" DeviceID:"+uuid);
        Map<String,Object> result=timeseriesService.getTimeseries(uuid,token);
        if(result.get("message")!=null){
            return new Result(FAIL.code, false, null,(String) result.get("message"));
        }
        //在线离线状态 onOffStatus
//        String lifeTs=(String) result.get("Life_ts");
//        if(lifeTs!=null){
//            cellVol.setOnOffStatus(judgeOnOffStatus(lifeTs));
//        }
        cellVol.setOnOffStatus(OnOffStatusService.getOnOffStatus(result,imei));

        // pack_SN
        cellVol.setPackNo((String) result.get("PACK_SN"));
        //单体电压
        cellVol.setCellVols((Map<String, Integer>)result.get("vols"));
        //电压收集时间
        if(result.get("rackTotalVoltage_ts")!=null){
            long ts = Long.parseLong((String)result.get("rackTotalVoltage_ts"));
            cellVol.setCollectTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(ts));
        }
        return new Result(SUCCESS.code, true, cellVol, msg);
    }

    /**
     * 3.6 GPS实时定位信息
     *
     * @param imei
     * @param token
     * @return
     */
    public Result getGpsInfo(String imei, String token) {
        GpsInfo gpsInfo = new GpsInfo();
        //先从映射表根据SN码获取UUID
//        String uuid=DeviceUtil.SNtoUUID.get(imei);
        String uuid = deviceService.getUUIDByImei(imei);
        log.info("imei:"+imei+" DeviceID:"+uuid);
        Map<String,Object> result=timeseriesService.getTimeseries(uuid,token);
        if(result.get("message")!=null){
            return new Result(FAIL.code, false, null,(String)result.get("message"));
        }
        //东西标志
        gpsInfo.setEwflag((String) result.get("ewflag"));
        //南北标志
        gpsInfo.setNsflag((String) result.get("nsflag"));
        //在线离线状态 onOffStatus
//        String lifeTs=(String)result.get("Life_ts");
//        if(lifeTs!=null){
//            gpsInfo.setOnOffStatus(judgeOnOffStatus(lifeTs));
//        }
        gpsInfo.setOnOffStatus(OnOffStatusService.getOnOffStatus(result,imei));
        // pack_SN
        gpsInfo.setPackNo((String) result.get("PACK_SN"));
        //IMEI
        gpsInfo.setImei(imei);
        //GPSModel

        //经纬度和filter经纬度
        gpsInfo.setLng((String) result.get("longitude"));
        gpsInfo.setFilterLng((String)result.get("longitude"));
        if(result.get("longitude_ts")!=null){
            long Longitude_ts = Long.parseLong((String) result.get("longitude_ts"));
            gpsInfo.setGpsCollectTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Longitude_ts));
            gpsInfo.setFilterGpsCollectTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Longitude_ts));
        }
        gpsInfo.setLat((String) result.get("latitude"));
        gpsInfo.setFilterLat((String) result.get("latitude"));
        if(result.get("latitude_ts")!=null){
            long Latitude_ts = Long.parseLong((String) result.get("latitude_ts"));
            gpsInfo.setGpsCollectTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Latitude_ts));
            gpsInfo.setFilterGpsCollectTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Latitude_ts));
        }
        // GPS上报周期--------------------------------------------------------暂定30
        gpsInfo.setGpsReportInterval(30);
        return new Result(SUCCESS.code,true,gpsInfo, "success");
    }

    /**
     * 3.7 获取历史GPS信息
     *
     * @param imei
     * @param startTs
     * @param endTs
     * @param token
     * @return
     */
    public Result getGpsHisInfo(String imei,long startTs,long endTs,String token){//---------------------------------------url待确定
        List<GpsHisInfo> gpsHisInfoList=new LinkedList<>();//最终返回结果
        String msg="success";//返回的信息，默认success
        //先从映射表根据SN码获取UUID
//        String uuid=DeviceUtil.SNtoUUID.get(imei);
        String uuid = deviceService.getUUIDByImei(imei);
        log.info("imei:"+imei+" DeviceID:"+uuid);
        String url=apiConfig.getDataApiPrefix()+uuid+
                apiConfig.getDataApiSuffix()+"?keys=latitude,longitude,distance,nsflag,ewflag&startTs="+startTs+"&endTs="+endTs+"&limit="+LIMIT;
//        String[] resultStr=HttpClientUtil.get_data(url,TokenUtil.token);//String 格式获取结果, [0]为statusCode，[1]为结果(结果可能不是json)
        log.info("url:"+url);
        String[] resultStr=HttpClientUtil.get_data(url,token);//String 格式获取结果, [0]为statusCode，[1]为结果(结果可能不是json)
        if(Integer.valueOf(resultStr[0])!=200){//没有正确拿到信息
            log.error("code:"+resultStr[0]+" result:"+resultStr[1]);
            try{//错误但是返回json格式
                //尝试拿数据
                if(resultStr[1]==null){
                    return new Result(FAIL.code, false,null,"result[1] is null!");
                }
                JSONObject jsonResult = JSONObject.parseObject(resultStr[1]);
                // 错误信息不为null但是没有message项
                if(!jsonResult.containsKey("message")){
                    return new Result(FAIL.code, false,null,resultStr[1]);
                }
                // 有message项
                return new Result(FAIL.code, false, null,jsonResult.get("message").toString());
            }
            catch (JSONException e){//错误而且返回的不是JSON格式
//                System.out.println("resultStr: "+resultStr);
                return new Result(FAIL.code,false,null,resultStr[1]);//存疑，直接返回错误结果还是设null完事
            }
        }
        else {//如果正确拿到结果
            //尝试拿数据
            JSONObject jsonResult = JSONObject.parseObject(resultStr[1]);
            System.out.println(resultStr[1]);
            try{
                //lng直接遍历获取，Lat根据获取的Lng的ts，找到对应的值获取，distance取<=这个ts的最大ts的值
//                Map<Long,String> distanceMap=new HashMap<>();
                JSONArray lngArray=jsonResult.getJSONArray("longitude");
                JSONArray latArray=jsonResult.getJSONArray("latitude");
                JSONArray distanceArray=jsonResult.getJSONArray("distance");
                JSONArray ewflagArray=jsonResult.getJSONArray("ewflag");
                JSONArray nsflagArray=jsonResult.getJSONArray("nsflag");

                System.out.println(latArray);
                if(latArray==null){//这个时间段内没有数据
                    return new Result(SUCCESS.code, true, gpsHisInfoList, "success");
                }
                // 存各个字段的ts
                List<String> latList=putTs(latArray);
                List<String> distanceList=putTs(distanceArray);
                List<String> ewflagList=putTs(ewflagArray);
                List<String> nsflagList=putTs(nsflagArray);

                for(int i=0;i<latArray.size();i++){//Lat的ts存到List里
                    latList.add(latArray.getJSONObject(i).get("ts").toString());
                }

//                if(distanceArray!=null){//distance的ts存到list里，同时存ts-value的map便于对list排序后查找对应distance
//                    for(int i=0;i<distanceArray.size();i++){
//                        distanceList.add(distanceArray.getJSONObject(i).get("ts").toString());
//                        distanceMap.put(Long.parseLong(distanceArray.getJSONObject(i).get("ts").toString()),
//                                        distanceArray.getJSONObject(i).get("value").toString());
//                    }
////                    for(Long key : distanceMap.keySet()){
////                        System.out.println("ts:"+key+" distance:"+distanceMap.get(key));
////                    }
//                }

                for(int i=0;i<lngArray.size();i++){//遍历Lng，根据ts查找对应Lat,distance,的index
                    String ts=lngArray.getJSONObject(i).get("ts").toString();
//                    int index=latList.indexOf(ts);
                    GpsHisInfo gpsHisInfo=new GpsHisInfo();
                    gpsHisInfo.setLng(TimeseriesService.dealHexAscii(TimeseriesService.cutPre(lngArray.getJSONObject(i).get("value").toString())));//Lng直接获取
                    // 其余的获取相同ts的，getSameTimeData仅返回去掉N的value，即cutPre以后的
                    long distance=Long.parseLong(getSameTimeData(ts,distanceList,distanceArray),16);
                    gpsHisInfo.setDistance(Double.valueOf(distance));
                    gpsHisInfo.setLat(TimeseriesService.dealHexAscii(getSameTimeData(ts,latList,latArray)));//Latitude
                    gpsHisInfo.setEwflag(TimeseriesService.dealHexAscii(getSameTimeData(ts,ewflagList,ewflagArray)));//ewflag
                    gpsHisInfo.setNsflag(TimeseriesService.dealHexAscii(getSameTimeData(ts,nsflagList,nsflagArray)));//nsflag
//                    //设置Lat
//                    if(index!=-1){
////                        if(index==i){
////                            System.out.println(ts);
////                        }
//                        gpsHisInfo.setLat(latArray.getJSONObject(index).get("value").toString());
//                    }
//                    else {
//                        gpsHisInfo.setLat(null);
//                    }
                    gpsHisInfo.setGpsCollectTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Long.parseLong(ts)));
                    //设置distance
//                    if(distanceArray==null){//如果没查到distance则返回0
//                        gpsHisInfo.setDistance(0.0);
//                    }else {//如果有distance，则取<=LngTs的最大distance中的ts
//                        long lngTs=Long.parseLong(ts);
//                        long maxTs=0;
//                        for(int j=0;j<distanceList.size();j++){//遍历distanceList，找比<=lngTs的最大distanceTs
//                            long distanceTs=Long.parseLong(distanceList.get(j));
//                            if(distanceTs<=lngTs&&distanceTs>maxTs){
//                                maxTs=distanceTs;
//                            }
//                        }
////                        System.out.println("maxTs:"+maxTs+" distance:"+distanceMap.get(maxTs));
//                        if(maxTs!=0){
//                            gpsHisInfo.setDistance(getKmDistance(Double.parseDouble(distanceMap.get(maxTs))));
//                        }else {
//                            gpsHisInfo.setDistance(0.0);
//                        }
//                    }
                    gpsHisInfoList.add(gpsHisInfo);

                }
                System.out.println("List size:"+gpsHisInfoList.size());
            } catch (NullPointerException e) {
                log.warn("Cannot get GpsHisInfo", e);
                log.info(resultStr[1]);
                msg = "Cannot get GpsHisInfo";
                return new Result(FAIL.code, false, null, msg);
            }
        }
        return new Result(SUCCESS.code, true, gpsHisInfoList, "success");
    }
    private List<String> putTs(JSONArray array){
        List<String> list =new LinkedList<>();
        for(int i=0;i<array.size();i++){//Lat的ts存到List里
            list.add(array.getJSONObject(i).get("ts").toString());
        }
        return list;
    }
    private String getSameTimeData(String ts,List<String> list, JSONArray array){
        int index=list.indexOf(ts);
        if(index==-1){
            return null;
        }
        String value=array.getJSONObject(index).get("value").toString();
        value=TimeseriesService.cutPre(value);//去掉开头的N
        return value;
    }



    /**
     * 3.8 查询实时告警
     *
     * @param imei
     * @return
     */
    public Result getAlarmInfo(String imei, String token) {
        //1.调用httpclient工具类的各种接口获取到需要的基本信息
        String msg = "success";//返回的信息，默认success
        //先从映射表根据SN码获取UUID
//        String uuid=DeviceUtil.SNtoUUID.get(imei);
        String uuid = deviceService.getUUIDByImei(imei);
        log.info("imei:"+imei+" DeviceID:"+uuid);
        Map<String,Object> result=timeseriesService.getTimeseries(uuid,token);

        if(result.get("message")!=null){
            return new Result(FAIL.code, false, null,(String) result.get("message"));
        }
        List<Alarm> alarmList = new LinkedList<>();
        if(result.get("firstError")==null||result.get("secondError")==null
                ||result.get("firstError").equals(INVALID_DATA)||result.get("secondError").equals(INVALID_DATA)){
            return new Result(FAIL.code, false, null, "Cannot get AlarmInfo");
        }
        long ts = Long.parseLong((String)result.get("firstError_ts"));
        int[] firstAlarm = hexToBinary((String) result.get("firstError"), 32);
        int[] secondAlarm = hexToBinary((String) result.get("secondError"), 32);
        for (int i = 0; i < firstAlarm.length; i++) {
            System.out.print(firstAlarm[i]);
        }
        System.out.println();
        for (int i = 0; i < secondAlarm.length; i++) {
            System.out.print(secondAlarm[i]);
        }
        String packNo=(String) result.get("PACK_SN");
        for (int i = 0; i < firstAlarm.length; i++) {//从高位到低位遍历
            //统计alarm位数
            if (firstAlarm[i] == 1) {
//                        alarmNum++;
                //[i]实际上为第length-1-i位
                log.info("Error bit: " + (firstAlarm.length - 1 - i));//i下标实际对应这个位数
                System.out.println(AlarmUtil.alarmNo[i] + "1" + " " + AlarmUtil.alarmName[i] + "一级报警");

                Alarm alarm = new Alarm(packNo,
                        AlarmUtil.alarmNo[i] + "1",//这两个也是从高位到低位存的
                        AlarmUtil.alarmName[i] + "一级报警",
                        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(ts),
                        null);
                alarmList.add(alarm);
            }
        }
        for (int i = 0; i < secondAlarm.length; i++) {//从高位到低位遍历
            //统计alarm位数
            if (secondAlarm[i] == 1) {
//                        alarmNum++;
                //[i]实际上为第length-1-i位
                log.info("Error bit: " + (secondAlarm.length - 1 - i));
                System.out.println(AlarmUtil.alarmNo[i] + "2" + " " + AlarmUtil.alarmName[i] + "二级报警");
                Alarm alarm = new Alarm(packNo,
                        AlarmUtil.alarmNo[i] + "2",
                        AlarmUtil.alarmName[i] + "二级报警",
                        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(ts),
                        null);
                alarmList.add(alarm);
            }
        }
        return new Result(SUCCESS.code,true,alarmList, msg);
    }

    /**
     * 3.9 查询电池开关状态
     *
     * @param imei
     * @param token
     * @return
     */
    public Result getBmsSwitch(String imei, String token) {
        //1.调用httpclient工具类的各种接口获取到需要的基本信息
        BmsSwitch bmsSwitch = new BmsSwitch();
        //先从映射表根据SN码获取UUID
//        String uuid=DeviceUtil.SNtoUUID.get(imei);
        String uuid = deviceService.getUUIDByImei(imei);
        log.info("imei:"+imei+" DeviceID:"+uuid);
        Map<String,Object> result=timeseriesService.getTimeseries(uuid,token);
        if(result.get("message")!=null){
            return new Result(FAIL.code, false, null,(String) result.get("message"));
        }
        //在线离线状态
//        bmsSwitch.setOnOffStatus(OnOffStatusService.getOnOffStatus(packNo, token));
//        String lifeTs=(String)result.get("Life_ts");
//        if(lifeTs!=null){
//            bmsSwitch.setOnOffStatus(judgeOnOffStatus(lifeTs));
//        }
        bmsSwitch.setOnOffStatus(OnOffStatusService.getOnOffStatus(result,imei));
//        if(result.get("mosSwitch")==null){//无法获取MOS状态
//            return new Result(FAIL.code,false,null, "Cannot get bmsSwitch");
//        }
//        int[] bmsSwitchCode = hexToBinary((String) result.get("mosSwitch"), 8);
        // 修改为inputSwitch的bit7(放电)和bit6(充电)
        if(result.get("inputSwitch")==null){//无法获取MOS状态
            return new Result(FAIL.code,false,null, "Cannot get bmsSwitch");
        }
        String inputSwitch=(String) result.get("inputSwitch");
        if(inputSwitch==null||inputSwitch.equals(INVALID_DATA)){
            return new Result(FAIL.code, false, null, "Cannot get bmsSwitch");
        }
        int[] bmsSwitchCode = hexToBinary(inputSwitch, 8);// 0到len-1分别为bit7到bit0
        System.out.println("inputSwitch:"+result.get("inputSwitch"));
        log.info("inputSwitch:"+result.get("inputSwitch"));
        if (bmsSwitchCode == null || bmsSwitchCode.length == 0) {
            return new Result(FAIL.code, false, null, "Cannot get bmsSwitch");//存疑，直接返回错误结果还是设null完事
        }
        //反向code[length-1=7]放电 code[length-2=6]充电
//        if (bmsSwitchCode[7] == 1) {//允许充电 bit0
//            bmsSwitch.setChargeSw(0);
//        } else {
//            bmsSwitch.setChargeSw(1);
//        }
//        if (bmsSwitchCode[6] == 1) {//允许放电 bit1
//            bmsSwitch.setDischargeSw(0);
//        } else {
//            bmsSwitch.setDischargeSw(1);
//        }
        // 修改为bit7=code[0]放电，bit6=code[1]充电，0允许1禁止
        bmsSwitch.setDischargeSw(bmsSwitchCode[0]);
        bmsSwitch.setChargeSw(bmsSwitchCode[1]);


        return new Result(SUCCESS.code, true, bmsSwitch, "success");
    }

    /**
     * 控制指令
     * @param imei
     * @param bmsControl
     * @return
     */
    public Result setbmsControl(String imei,Integer bmsControl){
        log.info("imei:"+imei);
        try {
            // 放电控制
            int dischargeControl=bmsControl;
            //转换为String类型16进制并拼接C2帧 0表示允许，1表示禁止，发送1则为0101，发送0则为0000
            String codeC2=decimalToHex(dischargeControl)+decimalToHex(dischargeControl);
            codeC2=LOCK.getControlCode()+codeC2+"0000000000";  // 填充后面的预留位

            // 获取客户
            String customerTitle=DeviceUtil.ImeiCustomerTitle.get(imei);
            log.info("Device controlled is {}",customerTitle);
            if(customerTitle==null||customerTitle.equals("")){
                return Result.fail("Cannot get Customer info");
            }

            // 构造完整消息，拼接customerTitle和C2
            String messageC2=TimeseriesService.stringToHexAscii(customerTitle)+codeC2;
            log.info("messageC2: "+messageC2);


            //发送控制并获取结果
            MqttUtil mqttUtil=new MqttUtil();
            String topicPub=ConstantUtil.TOPIC_PREFIX + imei + ConstantUtil.PUB_TOPIC_SUFFIX_CONTROL;
            String topicSub=ConstantUtil.TOPIC_PREFIX + imei + ConstantUtil.SUB_TOPIC_SUFFIX_CONTROL;
            log.info("topicPub:"+topicPub+" topicSub:"+topicSub);
            String response=mqttUtil.sendControlMessage(apiConfig.getMqttBroker(),messageC2,topicPub,topicSub,LOCK.getControlCode());
            log.info("Control Response:{}", response);
            // 对接收结果进行判断
            // BS53按原有逻辑 xx0101或xx0000 （已废弃）
            System.out.println("if BS53: "+"BS53".equals(customerTitle));
//            if("BS53".equals(customerTitle)){
//                return getSetResult(response,dischargeControl);
//            }
            // 非BS53直接判断第2个字节0100 xxxx xxxx xxxx或0101 xxxx xxxx xxxx
            return getSetResult2_5(response);

        }  catch (Exception e) {
            log.error("send MQTT message failed!",e);
            return Result.fail("send mqtt message failed!");
        }
    }
    public Result setBmsSwitch(String imei,Integer bmsControl,String token){//--------------------------------------------待修改
//        String topicPub="v1/" + sn + "/me/rpc/request";
//        String topicSub="v1/" + sn + "/me/telemetry";
        //sn获取UUID
//        String uuid=DeviceUtil.SNtoUUID.get(imei);
        String uuid = deviceService.getUUIDByImei(imei);
        log.info("imei:"+imei+" DeviceID:"+uuid);
        Map<String,Object> result=timeseriesService.getTimeseries(uuid,token);
        if(result.get("message")!=null){
            return new Result(FAIL.code, false, null,(String) result.get("message"));
        }
        //需要先获取0x04来获取当前的---------------------------------待修改
        if(result.get("inputSwitch")==null || result.get("inputSwitch").equals(INVALID_DATA)){//无法获取MOS状态
            return new Result(FAIL.code,false,null, "Cannot get Control info");
        }
        else {
            try{
                String mosSwitch=(String) result.get("inputSwitch");//这个相当于原来的 data.get("bmsSwitch")
                log.info("inputSwitch:"+mosSwitch);
                // 将MOS状态转换为二进制按位 0允许 1禁止
                int[] bmsSwitchCode = hexToBinary(mosSwitch, 8);
//                //bit0充电MOS状态，即code[7]，bit1放电Mos状态，即code[6]
//                //充电控制允许，从code里获取，需要01反转，和三方协议一致
//                int chargeControl=bmsSwitchCode[7]==0?1:0;
                // code[0]=bit7=放电状态，code[1]=bit6=充电状态，0允许1禁止，与三方相同
                // 充电控制，不需要反转
                int chargeControl=bmsSwitchCode[1];
                //放电控制允许，用户发送的获取，不需要反转
                int dischargeControl=bmsControl;
                //拼接C2帧 0表示允许，1表示禁止
                String codeC2=decimalToHex(chargeControl)+decimalToHex(dischargeControl);
                // 获取客户
                String customerTitle=DeviceUtil.ImeiCustomerTitle.get(imei);
                if(customerTitle==null||customerTitle.equals("")){
                    return new Result(FAIL.code,false,null, "Cannot get Customer info");
                }
                // 构造消息 拼接imei和C2
                customerTitle=TimeseriesService.stringToHexAscii(customerTitle);// 转换为16进制
                String messageC2=customerTitle+codeC2;
                log.info("messageC2"+messageC2);

                //发送控制并获取结果
                MqttUtil mqttUtil=new MqttUtil();
                String topicPub=ConstantUtil.TOPIC_PREFIX + imei + ConstantUtil.PUB_TOPIC_SUFFIX_CONTROL;
                String topicSub=ConstantUtil.TOPIC_PREFIX + imei + ConstantUtil.SUB_TOPIC_SUFFIX_CONTROL;
                log.info("topicPub:"+topicPub+" topicSub:"+topicSub);

                String response=mqttUtil.sendControlMessage(apiConfig.getMqttBroker(),messageC2,topicPub,topicSub,LOCK.getControlCode());
                //没有获取到反馈信息
                log.info("response:"+response);
                if(response==null){
                    //发送到mqtt启动轮询
                    return new Result(FAIL.code,false,null, "send success,but response timed out");
                }
                if(response.contains("message"))
                {
                    return new Result(FAIL.code,false,null, "MQTT消息推送失败");
                }
                System.out.println("response: "+response);

//                int success=Integer.parseInt(response.substring(32,34));// 待修改，改成C1的放电允许控制
                // 先获取后两个字节xx 01 01 xx xx xx xx xx
                if(response.length()>4){
                    response=response.substring(2,6);
                }
                System.out.println("cut response:"+response);
//                int success=response.equals(codeC2)?1:0;// 判断
                // 改为只需要判断放电
                String codeC2Discharge=decimalToHex(dischargeControl);
                String responseDischarge=response.substring(2);
                System.out.println("discharge set:"+ codeC2Discharge+" result:"+responseDischarge);
                int success=responseDischarge.equals(codeC2Discharge)?1:0;
                if(success==0){
                    return new Result(FAIL.code,false,null, "send success,but set failed");
//                    return new Result(FAIL.code,false,null, "resp:"+response);
                }
                // todo 选择性启用
//                return getSetResult(response,dischargeControl);
//                return getSetResult2_5(response);

            }catch (NullPointerException e){
                log.warn("Cannot get bmsSwitch",e);
                return new Result(FAIL.code,false,null, "Cannot get disChargeSW");
            } catch (Exception e) {
                log.error("oops!sth wrong!",e);
            }
            return new Result(SUCCESS.code, true, "true", "success");
        }
    }
    // 协议2.5直接通过C1的第二个字段判断是否设置成功
    private Result getSetResult2_5(String response){
        log.info("Protocol v2.5");
        if(response==null){
            //发送到mqtt启动轮询
            return Result.fail("send success,but response timed out");
        }
        if(response.contains("message"))
        {
            return Result.fail("MQTT消息推送失败");
        }
        // 获取控制结果字段
        if(response.length()>4){
            // xx 01 xx xx xx xx xx xx
            response=response.substring(2,4);
        }
        log.info("Control Result:{}",response);
        if(!"01".equals(response)){
            return Result.fail("send success,but set failed");
        }
        return Result.ok("true");
    }
    // 旧协议(协议2.5之前)通过对比C1 C2是否一样判断是否设置成功
    private Result getSetResult(String response,int dischargeControl){
        log.info("Protocol old");
        if(response==null){
            //发送到mqtt启动轮询
            return Result.fail("send success,but response timed out");
        }
        if(response.contains("message"))
        {
            return Result.fail("send MQTT message failed!");
        }
        System.out.println("response: "+response);
        // 先获取后两个字节xx0101
        if(response.length()>4){
            response=response.substring(2);
        }
        System.out.println("cut response:"+response);
        // 改为只需要判断放电
        String codeC2Discharge=decimalToHex(dischargeControl);
        // response现在是0101或0000，只取后两位(放电)
        String responseDischarge=response.substring(2);
        System.out.println("discharge set:"+ codeC2Discharge+" result:"+responseDischarge);
        int success=codeC2Discharge.equals(responseDischarge)?1:0;
        if(success==0){
            return Result.fail("send success,but set failed");
        }
        return Result.ok("true");
    }

    // 整数转换为1个字节的十六进制
    private static String decimalToHex(int decimal) {
        String hex = Integer.toHexString(decimal);
        return hex.length() == 1 ? "0" + hex : hex;
    }
    private static String setHexLowestBitTwo(String hexStr,int control) {
        int hex = Integer.parseInt(hexStr, 16);
        //控制倒数第二位bit1放电
        int newHex = (hex & 0xFFFD) | ((control & 1) << 1);
        System.out.println(Integer.toBinaryString(newHex));
        return String.format("%02X", newHex);
    }
    // 计算校验和
    private  static String makeCheckSum(String data){
        if (data == null || data.equals("")) {
            return "";
        }
        int total = 0;
        int len = data.length();
        int num = 0;
        while (num < len) {
            String s = data.substring(num, num + 2);
//            System.out.println(s);
            total += Integer.parseInt(s, 16);
            num = num + 2;
        }
        //用256求余最大是255，即16进制的FF
        int mod = total % 256;
        String hex = Integer.toHexString(mod);
        len = hex.length();
        // 如果不够校验位的长度，补0,这里用的是两位校验
        if (len < 2) {
            hex = "0" + hex;
        }
        return hex.toUpperCase();

    }



    /**
     * @author plyn
     * @param params 需要输入的请求参数
     * 获取设备基本信息，包括0x02,0x03....
     *               此处获取的均为原始信息，即带N开头的原始字符串
     */
    private Result getDeviceBasicInfo(String imei, String token, String params) {
//        String[] result = IMEIUtil.getLatestDevice(imei, token);//由传入的imei获取uuid
//        String uuid = result[0];
//        String msg = result[1];
//        String uuid=DeviceUtil.SNtoUUID.get(imei);
        String uuid = deviceService.getUUIDByImei(imei);
        if (uuid == null) {
            return new Result(FAIL.code, false, null, "Invalid UUID string: null");
        }


        String url = apiConfig.getDataApiPrefix() + uuid + apiConfig.getDataApiSuffix() + params;
        String[] resultStr = HttpClientUtil.get_data(url, token);
//        System.out.println("getDeviceBasicInfo:"+url);
        if (resultStr == null) {
            return new Result(FAIL.code, false, null, "未查询到数据");
        }

        JSONObject resultJson = JSON.parseObject(resultStr[1]);
        if(resultJson.containsKey("message")){
            return new Result(FAIL.code, false, null, resultJson.getString("message"));
        }

        return new Result(SUCCESS.code, true, resultJson, null);
    }

    /**
     * @author plyn
     * 获取充放电容量差
     */
    private static String electricSubtract(String electricStart, String electricEnd) {
        BigDecimal startBD = new BigDecimal(electricStart);
        BigDecimal endBD = new BigDecimal(electricEnd);
        // end - start
        return endBD.subtract(startBD).toString();
    }

    /**
     * @author plyn
     * 获取电池信息，暂时仅涉及累计充放电容量
     */
    private Result getBatteryInfo(String imei, String token, Date startTime, Date endTime, String params) {
        Result result = getDeviceBasicInfo(imei, token, params);
        if (!result.getSuccess())
            return result;
//        System.out.println("result"+result);
        // 获取设备基本信息
        JSONObject resultJson = (JSONObject) result.getData();

        Map<String, Object> resultData = new HashMap<>();

        // 存储其他基本信息
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        resultData.put(START_TIME, sdf.format(startTime));
        resultData.put(END_TIME, sdf.format(endTime));
        //------------------------------临时加的 后面再改↓
//        String[] uuidRS = IMEIUtil.getLatestDevice(imei, token);//由传入的imei获取uuid
//        String uuid = uuidRS[0];
//        String msg = uuidRS[1];
//        if (uuid == null) {
//            return new Result(FAIL.code, false, null, msg);
//        }
//        String uuid=DeviceUtil.SNtoUUID.get(imei);
        String uuid = deviceService.getUUIDByImei(imei);
        if (uuid == null) {
            return new Result(FAIL.code, false, null, "Invalid UUID string: null");
        }
        Map<String,Object> map=timeseriesService.getTimeseries(uuid,token);
        //------------------------------临时加的后面再改↑
        resultData.put(PACK_NO, (String)map.get("PACK_SN"));// @Pulsar 改为PACK_SN
        resultData.put(CURRENT_DAY, sdf.format(new Date()));
        resultData.put(TENANT_ID, null);// @Pulsar 预留为null


        // 解析并存储0x02信息
//        if (params.contains("0x02")) {
//            if (!resultJson.containsKey("0x02"))
//                return new Result(SUCCESS.code, true, resultData, "success");
//            resultData.putAll(decode0x02(resultJson));
//        }
        // @Pulsar 目前decode0x02仅涉及electricDischargeSum和electricChargeSum
        if(params.contains("electricChargeSum")&&params.contains("electricDischargeSum")){
//            if(!resultJson.containsKey("electricChargeSum")||!resultJson.containsKey("electricDischargeSum"))
//                return new Result(SUCCESS.code, true, resultData, "success");
//            resultData.putAll(decode0x02(resultJson));
            if(resultJson.containsKey("electricChargeSum"))
                resultData.putAll(decodeElectricChargeSum(resultJson));
            if(resultJson.containsKey("electricDischargeSum"))
                resultData.putAll(decodeElectricDischargeSum(resultJson));
        }

        // 解析并存储Battery_Status信息
        if (params.contains("Battery_Status")) {
            if (!resultJson.containsKey("Battery_Status"))
                return new Result(SUCCESS.code, true, resultData, "success");
            resultData.putAll(decodeBatteryStatus(resultJson));
        }

        // 解析并存储distance信息
        if (params.contains("distance")) {
            if (resultJson.containsKey("distance"))
                resultData.putAll(decodeDistance(resultJson));

        }

        return new Result(SUCCESS.code, true, resultData, "success");
    }

    /**
     * @author plyn
     * 从电池信息中解码0x02信息
     * => 暂时改为解析充放电量信息 =>拆分为两个单独的
     */
    private static Map<String, String> decode0x02(JSONObject info) {
//        // 获取设备0x02信息
//        JSONArray chargeInfo = (JSONArray) info.get("0x02");

//        // 获取startDate和endDate对应的0x02信息
//        String startDateValue = (String) chargeInfo.getJSONObject(0).get("value");
//        String endDateValue = (String) chargeInfo.getJSONObject(chargeInfo.size() - 1).get("value");
//
//        // 对设备0x02信息解码
//        Map<String, String> startDateInfo = DecodeUtil.decode_02(startDateValue);
//        Map<String, String> endDateInfo = DecodeUtil.decode_02(endDateValue);
//        String electricDisChargeStart = startDateInfo.getOrDefault("electricDischargeSum", "0");
//        String electricDisChargeEnd = endDateInfo.getOrDefault("electricDischargeSum", "0");
//        String electricChargeStart = startDateInfo.getOrDefault("electricChargeSum", "0");
//        String electricChargeEnd = endDateInfo.getOrDefault("electricChargeSum", "0");
        // @Pulsar 不再需要解码，改为直接获取充放电容量
        JSONArray chargeInfo=(JSONArray) info.get("electricChargeSum");
        JSONArray dischargeInfo=(JSONArray) info.get("electricDischargeSum");
        // 直接转换为单位为1AH的String表示的整数，与原来Decode获取相同---------@Pulsar，将0和size-1反过来了，因为array[0]才是最新数据
        String electricDisChargeStart = TimeseriesService.dealElectricSum((String) dischargeInfo.getJSONObject(dischargeInfo.size() - 1).get("value"));
        String electricDisChargeEnd = TimeseriesService.dealElectricSum((String) dischargeInfo.getJSONObject(0).get("value"));
        String electricChargeStart = TimeseriesService.dealElectricSum((String) chargeInfo.getJSONObject(chargeInfo.size() - 1).get("value"));
        String electricChargeEnd = TimeseriesService.dealElectricSum((String) chargeInfo.getJSONObject(0).get("value"));


        Map<String, String> resultData = new HashMap<>();
        // 放电存在无效数据
        if(electricDisChargeStart.equals(INVALID_DATA)||electricDisChargeEnd.equals(INVALID_DATA)){
            // startDate对应的累计放电容量
            resultData.put(ELECTRIC_DIS_CHARGE_START, electricDisChargeStart);
            // endDate对应的累计放电容量
            resultData.put(ELECTRIC_DIS_CHARGE_END, electricDisChargeEnd);
            // startDate至endDate之间的累计放电容量
            resultData.put(ELECTRIC_DIS_CHARGE_SUM, INVALID_DATA);
        }else {
            // startDate对应的累计放电容量
            resultData.put(ELECTRIC_DIS_CHARGE_START, electricDisChargeStart);
            // endDate对应的累计放电容量
            resultData.put(ELECTRIC_DIS_CHARGE_END, electricDisChargeEnd);
            // startDate至endDate之间的累计放电容量
            resultData.put(ELECTRIC_DIS_CHARGE_SUM, electricSubtract(electricDisChargeStart, electricDisChargeEnd));
        }
        // 充电存在无效数据
        if(electricChargeStart.equals(INVALID_DATA)||electricChargeEnd.equals(INVALID_DATA)){
            // startDate对应的累计充电容量
            resultData.put(ELECTRIC_CHARGE_START, electricChargeStart);
            // startDate对应的累计充电容量
            resultData.put(ELECTRIC_CHARGE_END, electricChargeEnd);
            // startDate至endDate之间的累计充电容量
            resultData.put(ELECTRIC_CHARGE_SUM, INVALID_DATA);
        }
        else {
            // startDate对应的累计充电容量
            resultData.put(ELECTRIC_CHARGE_START, electricChargeStart);
            // startDate对应的累计充电容量
            resultData.put(ELECTRIC_CHARGE_END, electricChargeEnd);
            // startDate至endDate之间的累计充电容量
            resultData.put(ELECTRIC_CHARGE_SUM, electricSubtract(electricChargeStart, electricChargeEnd));
        }
        return resultData;
    }

    /**
     * @author pulsar
     * 从电池信息中解码electricChargeSum信息，使用前要确保info中containsKey("electricChargeSum")
     */
    private static Map<String, String> decodeElectricChargeSum(JSONObject info) {
        JSONArray chargeInfo=(JSONArray) info.get("electricChargeSum");

        // 直接转换为单位为1AH的String表示的整数，与原来Decode获取相同
        String electricChargeStart = TimeseriesService.dealElectricSum((String) chargeInfo.getJSONObject(chargeInfo.size() - 1).get("value"));
        String startTs=chargeInfo.getJSONObject(chargeInfo.size() - 1).get("ts").toString();
        String electricChargeEnd = TimeseriesService.dealElectricSum((String) chargeInfo.getJSONObject(0).get("value"));
        String endTs=chargeInfo.getJSONObject(0).get("ts").toString();

        System.out.println("开始电量："+electricChargeStart+" startTs:"+startTs+ " 结束电量："+electricChargeEnd+" endTs:"+endTs);
        Map<String, String> resultData = new HashMap<>();
        if(electricChargeStart.equals(INVALID_DATA) || electricChargeEnd.equals(INVALID_DATA)){ // 存在无效数据
            // startDate对应的累计充电容量
            resultData.put(ELECTRIC_CHARGE_START, electricChargeStart);
            // startDate对应的累计充电容量
            resultData.put(ELECTRIC_CHARGE_END, electricChargeEnd);
            // startDate至endDate之间的累计充电容量
            resultData.put(ELECTRIC_CHARGE_SUM, INVALID_DATA);
        }
        else { // 数据全部有效
            // startDate对应的累计充电容量
            resultData.put(ELECTRIC_CHARGE_START, electricChargeStart);
            // startDate对应的累计充电容量
            resultData.put(ELECTRIC_CHARGE_END, electricChargeEnd);
            // startDate至endDate之间的累计充电容量
            resultData.put(ELECTRIC_CHARGE_SUM, electricSubtract(electricChargeStart, electricChargeEnd));
        }


        return resultData;
    }
    /**
     * @author pulsar
     * 从电池信息中解码electricDischargeSum信息，使用前要确保info中containsKey("electricDischargeSum")
     */
    private static Map<String, String> decodeElectricDischargeSum(JSONObject info) {
        JSONArray dischargeInfo=(JSONArray) info.get("electricDischargeSum");

        // 直接转换为单位为1AH的String表示的整数，与原来Decode获取相同 注意数组顺序0到size-1时间顺序为新到旧，即array[0]为endTs
        String electricDisChargeStart = TimeseriesService.dealElectricSum((String) dischargeInfo.getJSONObject(dischargeInfo.size() - 1).get("value"));
        String electricDisChargeEnd = TimeseriesService.dealElectricSum((String) dischargeInfo.getJSONObject(0).get("value"));

        Map<String, String> resultData = new HashMap<>();
        // 存在无效数据
        if(electricDisChargeStart.equals(INVALID_DATA)||electricDisChargeEnd.equals(INVALID_DATA)){
            // startDate对应的累计放电容量
            resultData.put(ELECTRIC_DIS_CHARGE_START, electricDisChargeStart);
            // endDate对应的累计放电容量
            resultData.put(ELECTRIC_DIS_CHARGE_END, electricDisChargeEnd);
            // startDate至endDate之间的累计放电容量
            resultData.put(ELECTRIC_DIS_CHARGE_SUM, INVALID_DATA);
        }else {
            // startDate对应的累计放电容量
            resultData.put(ELECTRIC_DIS_CHARGE_START, electricDisChargeStart);
            // endDate对应的累计放电容量
            resultData.put(ELECTRIC_DIS_CHARGE_END, electricDisChargeEnd);
            // startDate至endDate之间的累计放电容量
            resultData.put(ELECTRIC_DIS_CHARGE_SUM, electricSubtract(electricDisChargeStart, electricDisChargeEnd));
        }

        return resultData;
    }

    /**
     * @author pulsar
     * 从电池信息中解码electricChargeSum信息，使用前要确保info中containsKey("electricChargeSum")
     */
    private static Map<String, String> decodeRackTotalVoltage(JSONObject info) {
        JSONArray rackTotalVoltageInfo=(JSONArray) info.get("rackTotalVoltage");

        // 直接转换为单位为1AH的String表示的整数，与原来Decode获取相同，注意array[0]才是最新的，即endTs为0，startTs为size-1
        String rackTotalVoltageStart = TimeseriesService.dealRackTotalVoltage((String) rackTotalVoltageInfo.getJSONObject(rackTotalVoltageInfo.size() - 1).get("value"));
        String startTs= rackTotalVoltageInfo.getJSONObject(rackTotalVoltageInfo.size() - 1).get("ts").toString();
        String rackTotalVoltageEnd = TimeseriesService.dealRackTotalVoltage((String) rackTotalVoltageInfo.getJSONObject(0).get("value"));
        String endTs=rackTotalVoltageInfo.getJSONObject(0).get("ts").toString();

        System.out.println("开始电压："+rackTotalVoltageStart+" startTs:"+startTs+ " 结束电压："+rackTotalVoltageEnd+" endTs:"+endTs);
        Map<String, String> resultData = new HashMap<>();
        // startDate对应的电压
        resultData.put(TOTAL_VOLTAGE_START, rackTotalVoltageStart);
        // startDate对应的电压
        resultData.put(TOTAL_VOLTAGE_END, rackTotalVoltageEnd);

        return resultData;
    }



    /**
     * @author plyn
     * 从电池信息中解码Battery_Status信息
     */
    private static Map<String, String> decodeBatteryStatus(JSONObject info) {
        // 获取设备Battery_Status信息集合
        JSONArray chargeInfo = (JSONArray) info.get("Battery_Status");

        // 统计不同Battery_Status出现的次数
        AtomicInteger fastCharge = new AtomicInteger(0);
        AtomicInteger disCharge = new AtomicInteger(0);
        AtomicInteger maintenance = new AtomicInteger(0);
        AtomicInteger noLoad = new AtomicInteger(0);
        AtomicInteger insert = new AtomicInteger(0);
        AtomicInteger storage = new AtomicInteger(0);

        // 获取Battery_Status信息集合,暂时没包含文档中未说明的Battery_Status
        chargeInfo.forEach((obj) -> {
            JSONObject jsonObject = (JSONObject) obj;
//            Integer batteryStatus = jsonObject.getInteger("value");
            Integer batteryStatus=Integer.parseInt(TimeseriesService.dealBatteryStatus((String)jsonObject.get("value")));// @Pulsar 返回10进制表示的整数
            if (batteryStatus.equals(67))
                fastCharge.getAndIncrement();
            else if (batteryStatus.equals(68))
                disCharge.getAndIncrement();
            else if (batteryStatus.equals(78))
                noLoad.getAndIncrement();

        });

        Map<String, String> resultData = new HashMap<>();
        resultData.put(Fields.FAST_CHARGE, fastCharge.toString());
        resultData.put(DIS_CHARGE, disCharge.toString());
        resultData.put(MAINTENANCE, maintenance.toString());
        resultData.put(NO_LOAD, noLoad.toString());
        resultData.put(INSERT, insert.toString());
        resultData.put(STORAGE, storage.toString());

        return resultData;
    }

    /**
     * @author plyn
     * 从电池信息中解码distance信息，使用前要确保info中containsKey("distance")
     */
    private static Map<String, String> decodeDistance(JSONObject info) {
        // 获取设备distance信息
        JSONArray distanceInfo = (JSONArray) info.get("distance");

        // 获取startDate和endDate对应的distance信息
//        Double startDateValue = Double.valueOf((String) distanceInfo.getJSONObject(0).get("value"));
//        Double endDateValue = Double.valueOf((String) distanceInfo.getJSONObject(distanceInfo.size() - 1).get("value"));
//        Double distance = getKmDistance(endDateValue - startDateValue);// @Pulsar 套了一层除以1000的米转千米

        // @Pulsar 获取到的distance为带N的原字符串，处理后为String表示的10进制整数 @Pulsar 时间顺序与数组顺序相反，array[0]才是endTime
        Double startDateValue = Double.valueOf(TimeseriesService.dealDistance((String) distanceInfo.getJSONObject(distanceInfo.size() - 1).get("value")));
        Double endDateValue = Double.valueOf(TimeseriesService.dealDistance((String) distanceInfo.getJSONObject(0).get("value")));
        Double distance = endDateValue - startDateValue;// @Pulsar 现在原单位就是KM，没看懂

        Map<String, String> resultData = new HashMap<>();
        resultData.put(DISTANCE_SUM, distance.toString());
        resultData.put(DISTANCE_START,startDateValue.toString());
        resultData.put(DISTANCE_END,endDateValue.toString());

        return resultData;
    }

    /**
     * @author pulsar
     * 从电池信息中解码travelTime信息，使用前要确保info中containsKey("travelTime")
     */
    private static Map<String, String> decodeTravelTime(JSONObject info) {//------------------------------------------------------暂未完成，待修改为TravelTime版本
        // 获取设备distance信息
        JSONArray distanceInfo = (JSONArray) info.get("travelTime");

        // @Pulsar 获取到的distance为带N的原字符串，处理后为String表示的10进制整数-----------------------------------------------------待修改为TravelTime版本
        Double startDateValue = Double.valueOf(TimeseriesService.dealDistance((String) distanceInfo.getJSONObject(distanceInfo.size() - 1).get("value")));
        Double endDateValue = Double.valueOf(TimeseriesService.dealDistance((String) distanceInfo.getJSONObject(0).get("value")));
        Double travelTime = endDateValue - startDateValue;// ---------------------------------------------单位未确定

        Map<String, String> resultData = new HashMap<>();
        resultData.put(TRAVEL_TIME_SUM, travelTime.toString());
        resultData.put(TRAVEL_TIME_START,startDateValue.toString());
        resultData.put(TRAVEL_TIME_END,endDateValue.toString());

        return resultData;
    }

    /**
     * @author plyn
     * 对Result结果进行筛选
     */
    private Map<String, String> filterData(Map<String, String> data, List<String> params) {
        Map<String, String> resultData = new HashMap<>();

        params.forEach((param) -> {
            if (data.containsKey(param))
                resultData.put(param, data.get(param));
            else
                resultData.put(param, String.valueOf(0));
        });

        return resultData;
    }

    private List<Date> splitTime(Date startTime, Date endTime) {
        long startTs = startTime.getTime();
        long endTs = endTime.getTime();
        List<Date> startTimeList = new ArrayList();
        while (startTs <= endTs){
            startTimeList.add(new Date(startTs));
            startTs+=ONE_DAY;
        }

        return startTimeList;
    }

    /**
     * @author plyn
     * 3.3 日里程和电量统计
     * 暂时未完成，仅返回currentDay，chargeSum和disChargeSum
     */
    public Result calCarDistanceDay(String imei, String token, Date startTime, Date endTime) {
        // 将endTime延后一天，因为传入时间为当日0点
        long endTs = endTime.getTime() + ONE_DAY;
//        String params = "?keys=0x02,distance&startTs=" + startTime.getTime() + "&endTs=" + endTime.getTime();
        // @Pulsar 0x02拆分成了充放电量
        String params = "?keys=electricChargeSum,electricDischargeSum,distance&startTs=" + startTime.getTime() + "&endTs=" + endTime.getTime();

        // 获取电池基本信息
        Result batteryInfo = getBatteryInfo(imei, token, startTime, endTime, params);
        if (!batteryInfo.getSuccess())
            return batteryInfo;
        Map<String, String> data = (Map<String, String>) batteryInfo.getData();

        // 对返回结果进行筛选
        String[] returnParams = {PACK_NO, CURRENT_DAY, DISTANCE_SUM, ELECTRIC_CHARGE_SUM, ELECTRIC_DIS_CHARGE_SUM, TENANT_ID};
        Map<String, String> resultData = filterData(data, Arrays.asList(returnParams));

        // 由于返回字段名不同，单独进行转换
        if (resultData.containsKey(PACK_NO)) {
            resultData.put("packNo", resultData.get(PACK_NO));
            resultData.remove(PACK_NO);
        }
        if (resultData.containsKey(ELECTRIC_CHARGE_SUM)) {
            resultData.put("chargeSum", resultData.get(ELECTRIC_CHARGE_SUM));
            resultData.remove(ELECTRIC_CHARGE_SUM);
        }
        if (resultData.containsKey(ELECTRIC_DIS_CHARGE_SUM)) {
            resultData.put("disChargeSum", resultData.get(ELECTRIC_DIS_CHARGE_SUM));
            resultData.remove(ELECTRIC_DIS_CHARGE_SUM);
        }
        if (resultData.containsKey(DISTANCE_SUM)) {
            resultData.put("distance", resultData.get(DISTANCE_SUM));
            resultData.remove(DISTANCE_SUM);
        }

        return new Result(SUCCESS.code, true, resultData, batteryInfo.getMsg());
    }

    /**
     * @author plyn
     * 3.3.2 日里程和电量统计
     * 与3.3区别在于将时间段分成n个一天进行查询
     * 暂时无用
     */
    public Result calCarDistanceDay2(String imei, String token, Date startTime, Date endTime) {
        // 将endTime延后一天，因为传入时间为当日0点
//        long endTs = endTime.getTime() + ONE_DAY;
        List<Date> startTs = splitTime(startTime, endTime);
        Map<String, String> resultData = new HashMap<>();
        startTs.forEach((startT) -> {
            Result result = calCarDistanceDay(imei, token, startT, new Date(startT.getTime() + ONE_DAY));
            if (!result.getSuccess())
                return;
        });

        return new Result(SUCCESS.code, true, resultData, "success");
    }

    /**
     * @author plyn
     * 获取某状态开始时间，即获取StatusChange对应的时间，同时获取变化
     */
    private Result getStartTime(String imei, String token, Integer chargeStatus) {
        System.out.println("getStartTime...");
        // 传入充放电状态对应的状态转换码 chargeStatus可能取值：0放电 1快充 4空载
        List<Integer> statusChangeCodeList = ChargeStatusUtil.getStatusChangeCodeList(chargeStatus); // 传入要查询的状态，获取对应的状态转换码
        System.out.println("statusChangeCodeList:"+statusChangeCodeList);
        if (statusChangeCodeList.contains(0))
            return new Result(FAIL.code, false, null, "充放电状态输入错误");
        int statusChangeCode = -1;
        long statusChangeTime = 0;

        // 当前状态转换码不属于【传入状态】的状态转换码，则获取上一个【时间段（一天）】的状态转换码列表，直到开始时间为据现在
        long currentTime = new Date().getTime();
        boolean ifStop = false;
        for (int i = 1; i < QUERY_TIME_LIMIT;i += 3){ // 上限查询30天
            long startTime = currentTime - ONE_DAY * 3; // 每次查3天
//            String params = "?keys=StatusChange&startTs=" + startTime + "&endTs=" + currentTime;
            String params = "?keys=chargeFlag&startTs=" + startTime + "&endTs=" + currentTime+"&limit="+LIMIT;
            System.out.println("startTime:"+DateUtil.timestampToString(startTime)+" endTime:"+DateUtil.timestampToString(currentTime));
            Result deviceBasicInfo = getDeviceBasicInfo(imei, token, params);
            if (!deviceBasicInfo.getSuccess())
                return deviceBasicInfo;

            // 获取该时间段状态转换码chargeFlag集合
            JSONObject resultJson = (JSONObject) deviceBasicInfo.getData();
//            JSONArray statusChangeCodeArray = (JSONArray) resultJson.get("StatusChange");
            JSONArray statusChangeCodeArray = (JSONArray) resultJson.get("chargeFlag");
            if (statusChangeCodeArray == null){
                currentTime = startTime;
                continue;
            }

            int size=statusChangeCodeArray.size();
            // 获取 最后一个 【属于statusChangeCodeList】的状态转换码
            // 按时间顺序从前往后，则按数组顺序从后往前，从当前三天中寻找开始标志
            for(int j=size-1;j>=0;j--){
                Object jsonObj=statusChangeCodeArray.getJSONObject(j);
                if(TimeseriesService.dealChargeFlag(((JSONObject)jsonObj).get("value").toString())==null||
                        TimeseriesService.dealChargeFlag(((JSONObject)jsonObj).get("value").toString()).equals(INVALID_DATA)){
                    continue;
                }
                int code = Integer.parseInt(TimeseriesService.dealChargeFlag(((JSONObject)jsonObj).get("value").toString()));
                System.out.print(code+" ");
                if (statusChangeCodeList.contains(code)){ // 从头遍历到尾来找最后一个，找到了内层循环不停，直到全遍历完
                    statusChangeCode = code;
                    statusChangeTime = ((JSONObject)jsonObj).getLongValue("ts");
                    ifStop = true; // 只要有外层循环就要停
                }
            }
            System.out.println();
//            for (Object jsonObj: statusChangeCodeArray) {
////                int code = ((JSONObject)jsonObj).getIntValue("value");
//                int code = Integer.parseInt(TimeseriesService.dealChargeFlag(((JSONObject)jsonObj).get("value").toString()));
//                if (statusChangeCodeList.contains(code)){ // 从头遍历到尾来找最后一个，找到了内层循环不停，直到全遍历完
//                    statusChangeCode = code;
//                    statusChangeTime = ((JSONObject)jsonObj).getLongValue("ts");
//                    ifStop = true; // 只要有外层循环就要停
//                }
//            }
            if (ifStop)
                break;
            else
                currentTime = startTime; // 当前查的3天没有则currentTime改为此时的startTime，再往前查3天
        }

        Map<String, Object> resultData = new HashMap<>();
        System.out.println("statusChangeCode:"+statusChangeCode+" statusChangeTime："+statusChangeTime);
        resultData.put("statusChangeCode", statusChangeCode); // 找到的chargeFlag
        resultData.put("statusChangeTime", statusChangeTime); // 找到的开始时间

        return new Result(SUCCESS.code, true, resultData, "success");
    }
    private Result getEndTime(String imei, String token,long startTime ,Integer startStatus){
        System.out.println("getEndTime...");
        // 传入充放电状态对应的状态转换码 chargeStatus可能取值：0放电 1快充 4空载
        List<Integer> statusChangeCodeList = ChargeStatusUtil.getStatusChangeCodeEndList(startStatus); // 传入开始状态，查询结束时间
        System.out.println("statusChangeCodeList:"+statusChangeCodeList);
        if (statusChangeCodeList.contains(0))
            return new Result(FAIL.code, false, null, "充放电开始状态错误");
        int statusChangeCode = -1;
        Long statusChangeTime = new Date().getTime();
        // 直接从startTime查到现在
        long endTime=new Date().getTime();
        String params = "?keys=chargeFlag&startTs=" + startTime + "&endTs=" + endTime+"&limit="+LIMIT;
        System.out.println("startTime:"+DateUtil.timestampToString(startTime)+" endTime:"+DateUtil.timestampToString(endTime));
        Result deviceBasicInfo = getDeviceBasicInfo(imei, token, params);
        if (!deviceBasicInfo.getSuccess())
            return deviceBasicInfo;
        JSONObject resultJson = (JSONObject) deviceBasicInfo.getData();
        JSONArray statusChangeCodeArray = (JSONArray) resultJson.get("chargeFlag");
        // 按时间顺序从后往前，则数组顺序从前往后
        for (Object jsonObj: statusChangeCodeArray) {
            System.out.println(jsonObj);
//               int code = ((JSONObject)jsonObj).getIntValue("value");
            if(TimeseriesService.dealChargeFlag(((JSONObject)jsonObj).get("value").toString())==null||
                    TimeseriesService.dealChargeFlag(((JSONObject)jsonObj).get("value").toString()).equals(INVALID_DATA)){
                continue;
            }
            int code = Integer.parseInt(TimeseriesService.dealChargeFlag(((JSONObject)jsonObj).get("value").toString()));
//            System.out.println("jsonObj:"+jsonObj+" code:"+code);
            if (statusChangeCodeList.contains(code)){ // 从头遍历到尾来找第一个，找到了马上两层循环全部退出
                statusChangeCode = code;
                statusChangeTime = ((JSONObject)jsonObj).getLongValue("ts");
                System.out.println("Break:"+jsonObj);
                    break;
                }
        }
        System.out.println();
        Map<String, Object> resultData = new HashMap<>();
        System.out.println("statusChangeCode:"+statusChangeCode+" statusChangeTime："+statusChangeTime);
        resultData.put("statusChangeCodeEnd", statusChangeCode); // 找到的chargeFlag
        resultData.put("statusChangeTimeEnd", statusChangeTime); // 找到的开始时间

        return new Result(SUCCESS.code, true, resultData, "success");
    }

    private Result getCurrentChargeStatus(String imei, String token) {
        String params = "?keys=Battery_Status";
        Result result = getDeviceBasicInfo(imei, token, params);
        if (!result.getSuccess())
            return result;

        JSONObject resultJson = (JSONObject) result.getData();
        Object kv = ((JSONArray)resultJson.get("Battery_Status")).get(0);
//        result.setData(((JSONObject)kv).get("value"));
        // 无效数据，即状态为-1
        if(TimeseriesService.dealBatteryStatus((String)((JSONObject)kv).get("value")).equals(INVALID_DATA)){
            return new Result(FAIL.code,false,null,"Battery Status Invalid!");
        }
        result.setData(TimeseriesService.dealBatteryStatus((String)((JSONObject)kv).get("value")));//@Pulsar 返回String表示的10进制整数可能取值：67充电 68放电 78空载
        return result;
    }

    /**
     * @author plyn
     * 3.10 查询当前（最后一次）充放电量
     */
    public Result getElectricLatest(String imei, String token, Integer chargeStatus) {//------------------------------待修改
        Result result = getCurrentChargeStatus(imei, token); // 'C'-充电67 'D'-放电68 'N'-静置78
        if(result.getData()==null){
            return result;
        }
        Integer currentChargeStatus = Integer.parseInt((String) result.getData()) ;  // 查询最后一次充电还是放电
        if (chargeStatus == null) {
            // 若未传入chargeStatus，则以当前充放电状态为chargeStatus
            chargeStatus = ChargeStatusUtil.decode(currentChargeStatus);
        }
        System.out.println("getElectricLatest, status:"+chargeStatus);
        /**
         * chargeStatus 可能取值: 0放电 1快充 4空载
         */
        log.info("chargeStatus:"+chargeStatus);
        // 获取此状态开始时间
        Result startTimeResult = getStartTime(imei, token, chargeStatus);
        if (!startTimeResult.getSuccess())
            return startTimeResult;

        Map<String, Object> startTimeData = (Map<String, Object>) startTimeResult.getData();
        int statusChangeCode = (int) startTimeData.get("statusChangeCode"); // 最近一次的充电或者放电开始的chargeFlag，确定是充电还是放电
        long statusChangeTime = (long) startTimeData.get("statusChangeTime"); // 最近一次的充(放)电开始时间


        long currentTime = new Date().getTime();
        long startTime = statusChangeTime;
        Long endTime=null;
        String msg = null;
        /**
         * code = -1表示两种情况：
         * 1.从QUERY_TIME_LIMIT天前一直处于该状态，此时认为此状态开始时间为【当前时间-QUERY_TIME_LIMIT】，此状态结束时间为【空】
         * 2.从QUERY_TIME_LIMIT天不存在与该状态相关的转换，此时认为QUERY_TIME_LIMIT内没有最近一次充(放)电记录，此时结束时间为【当前时间】
         *
         * code != -1表示QUERY_TIME_LIMIT天内有状态变化
         */
        if (statusChangeCode == -1) {
            if (!chargeStatus.equals(currentChargeStatus)) // 此时为情况2，不存在相关状态转换
                return new Result(FAIL.code, false, null, QUERY_TIME_LIMIT+"天内未出现该状态");

            // 此时为情况1，整个QUERY_TIME_LIMIT天内一直处于该状态
            startTime = currentTime - QUERY_TIME_LIMIT * ONE_DAY;
            endTime=currentTime;
//            msg = QUERY_TIME_LIMIT + "天内未发生状态变化";
            msg="success"; // @Pulsar  暂时按甲方要求修改
        } else { // statusCode!=-1，此时可以获取到结束时间
            Result endTimeResult=getEndTime(imei,token,startTime,statusChangeCode);
            if(!endTimeResult.getSuccess())
                return endTimeResult;
            Map<String, Object> endTimeData = (Map<String, Object>) endTimeResult.getData();
            int statusChangeCodeEnd = (int) endTimeData.get("statusChangeCodeEnd"); // 最近一次的充电或者放电结束的chargeFlag
            long statusChangeTimeEnd = (long) endTimeData.get("statusChangeTimeEnd"); // 最近一次的充(放)电结束
            endTime=statusChangeTimeEnd;
        }

//        String params = "?keys=0x02&startTs=" + startTime + "endTs=" + currentTime;
        String params = "?keys=electricChargeSum,electricDischargeSum&startTs=" + startTime + "endTs=" + endTime;//@Pulsar 目前0x02仅含有充放电量

        // 获取电池基本信息
        Result batteryInfo = getBatteryInfo(imei, token, new Date(startTime), new Date(endTime), params);
        if (!batteryInfo.getSuccess())
            return batteryInfo;
        Map<String, String> data = (Map<String, String>) batteryInfo.getData();

        // 对返回结果进行筛选
        String[] returnParams = {ELECTRIC_CHARGE_START, ELECTRIC_CHARGE_END,ELECTRIC_CHARGE_SUM,
                ELECTRIC_DIS_CHARGE_START , ELECTRIC_DIS_CHARGE_END, ELECTRIC_DIS_CHARGE_SUM,
                PACK_NO, START_TIME, END_TIME
        };
        Map<String, String> resultData = filterData(data, Arrays.asList(returnParams));
        if (resultData.containsKey(PACK_NO)) {
            resultData.put("packNo", resultData.get(PACK_NO));
            resultData.remove(PACK_NO);
        }

//        resultData.put(CHARGE_STATUS, ChargeStatusUtil.decode(chargeStatus).toString());
        resultData.put("chargeStatus", String.valueOf(chargeStatus));
        if (statusChangeCode == -1)
            resultData.put(END_TIME, null);


        return new Result(SUCCESS.code, true, resultData, msg);

    }

    /**
     * @author plyn
     * 3.11 历史充放电电量查询
     */
    public Result getElectric(String imei, String token, Date startTime, Date endTime) {
        Map<String, Object> resultData = new HashMap<>();

        // 暂未实现
        resultData.put(RECORDS, null);
        resultData.put(TOTAL, null);
        resultData.put(SIZE, null);
        resultData.put(CURRENT, null);
        resultData.put(PAGES, null);

        return new Result(SUCCESS.code, true, resultData, null);
    }

    /**
     * @author plyn
     * 3.12 历史充放电电量汇总查询
     */
    public Result getElectricSum(String imei, String token, Date startTime, Date endTime, Integer chargeStatus) {
//        String params = "?keys=0x02,distance,Battery_Status&startTs=" + startTime.getTime() + "&endTs=" + endTime.getTime();
        String params = "?keys=electricChargeSum,electricDischargeSum,distance,Battery_Status&startTs=" + startTime.getTime() + "&endTs=" + endTime.getTime();
        // 获取电池基本信息
        Result batteryInfo = getBatteryInfo(imei, token, startTime, endTime, params);
        if (!batteryInfo.getSuccess())
            return batteryInfo;
        Map<String, String> data = (Map<String, String>) batteryInfo.getData();

        // 对返回结果进行筛选
        String[] returnParams = {ELECTRIC_CHARGE_SUM, ELECTRIC_DIS_CHARGE_SUM, DISTANCE_SUM};
        Map<String, String> resultData = filterData(data, Arrays.asList(returnParams));

        // 额外参数
        long duration = endTime.getTime() - startTime.getTime();
        resultData.put(DURATION, String.valueOf(duration/60000));
//        resultData.put(CHARGE_STATUS, String.valueOf(ChargeStatusUtil.decode(chargeStatus)));
        resultData.put("chargeStatus", String.valueOf(ChargeStatusUtil.decode(chargeStatus)));

        if (chargeStatus == 67)
            resultData.put(CHARGE_STATUS_NUM, data.get("fastCharge"));
        else if (chargeStatus == 68)
            resultData.put(CHARGE_STATUS_NUM, data.get("disCharge"));
        else if (chargeStatus == 78)
            resultData.put(CHARGE_STATUS_NUM, data.get("noLoad"));

        return new Result(SUCCESS.code, true, resultData, "success");
    }

    /**
     * extra 1 电池使用统计报表
     */
    public Result getUsageInfo(String imei,String date,String token){
        // 由imei获取uuid
        String uuid=deviceService.getUUIDByImei(imei);
        UsageInfo usageInfo=new UsageInfo();
        // packNo
        usageInfo.setPackNo(timeseriesService.getPackSN(uuid,token).get("PACK_SN"));
        // imei
        usageInfo.setImei(imei);
        // 获取设备创建时间，用于计算日均数据
        Long createdTs=DeviceUtil.getDeviceCreatedTime(imei);
        if(createdTs==null){
            log.warn("Cannot get created time");
            return new Result(FAIL.code, false, null, "Invalid UUID string: null");
        }
        long nowTs=System.currentTimeMillis();
        int totalDays=DateUtil.getDaysBetween(createdTs,nowTs);
        // 前日起始时间
        String yesterday=DateUtil.getPreviousDay(date);
        long[] yesterdayTs=DateUtil.getDayStartAndEndTimestamp(yesterday);
        String startTimeYes=DateUtil.timestampToString(yesterdayTs[0]);
        String endTimeYes=DateUtil.timestampToString(yesterdayTs[1]);
        // 今日起始时间
        long[] todayTs=DateUtil.getDayStartAndEndTimestamp(date);
        String startTimeToday=DateUtil.timestampToString(todayTs[0]);
        String endTimeToday=DateUtil.timestampToString(todayTs[1]);
        System.out.println("Yesterday:"+startTimeYes+" Today:"+startTimeToday);
        // 行驶里程、行驶时长、充电标志用于获取充电次数和时长、充电电量
//        String keys="distance,travelTime,chargeFlag,electricChargeSum";
        String keys="distanceToday,travelTimeToday,chargeNumToday,chargeTimeToday,chargeSumToday,averageChargeTimeToday,chargeTravelRatioToday";
        // 查询前日数据
        String paramsYes="?keys="+keys+"&startTs="+yesterdayTs[0]+"&endTs="+yesterdayTs[1];
        Result rsYes=timeseriesService.getTimeseriesHis(uuid,token,paramsYes);
        if(!rsYes.getSuccess()){
            return new Result(FAIL.code,false,null,rsYes.getMsg());
        }else {
            Map<String,String> resultData=new HashMap<>();// 保存计算结果
            JSONObject resultJson = (JSONObject) rsYes.getData();
            // 注意！数据从0到size-1为从新到旧！！！
            // 行驶里程
//            if (resultJson.containsKey("distance")){
//                resultData.putAll(decodeDistance(resultJson));  // 累计里程DISTANCE_SUM
//                usageInfo.setDistanceYesterday(Double.valueOf(resultData.get(DISTANCE_SUM)));
//            }
            if (resultJson.containsKey("distanceToday")){
                JSONArray distanceInfo = (JSONArray) resultJson.get("distanceToday");
                String distanceToday=TimeseriesService.dealDistance((String) distanceInfo.getJSONObject(0).get("value"));
                if(distanceToday!=null){
                    usageInfo.setDistanceYesterday(Double.valueOf(distanceToday));
                }
            }
            // 行驶时长
//            if (resultJson.containsKey("travelTime")){
//                resultData.putAll(decodeTravelTime(resultJson));  // 累计里程DISTANCE_SUM
//                usageInfo.setTravelTimeYesterday(Double.valueOf(resultData.get(TRAVEL_TIME_SUM)));
//            }
            if (resultJson.containsKey("travelTimeToday")){
                JSONArray travelTimeInfo = (JSONArray) resultJson.get("travelTimeToday");
                String travelTime=TimeseriesService.dealDischargeTime((String) travelTimeInfo.getJSONObject(0).get("value"));
                if(travelTime!=null){
                    usageInfo.setTravelTimeYesterday(Double.valueOf(travelTime));
                }
            }
            // 充电次数
            if (resultJson.containsKey("chargeNumToday")){
                JSONArray chargeNumInfo = (JSONArray) resultJson.get("chargeNumToday");
                String chargeNum=TimeseriesService.dealChargeNum((String) chargeNumInfo.getJSONObject(0).get("value"));
                if(chargeNum!=null){
                    usageInfo.setChargeNumYesterday(Long.parseLong(chargeNum));
                }
            }
            // 充电时长
            if (resultJson.containsKey("chargeTimeToday")){
                JSONArray chargeTimeInfo = (JSONArray) resultJson.get("chargeTimeToday");
                String chargeTime=TimeseriesService.dealChargeTime((String) chargeTimeInfo.getJSONObject(0).get("value"));
                if(chargeTime!=null){
                    usageInfo.setChargeTimeYesterday(Double.valueOf(chargeTime));
                }
            }
            // 充电电量
//            if(resultJson.containsKey("electricChargeSum")){
//                resultData.putAll(decodeElectricChargeSum(resultJson));
//                usageInfo.setChargeSumYesterday(Double.valueOf(resultData.get(ELECTRIC_CHARGE_SUM)));
//            }
            if(resultJson.containsKey("chargeSumToday")){
                JSONArray chargeInfo=(JSONArray) resultJson.get("chargeSumToday");
                String chargeSumToday=TimeseriesService.dealElectricSum((String) chargeInfo.getJSONObject(0).get("value"));
                if(chargeSumToday!=null){
                    usageInfo.setChargeSumYesterday(Double.valueOf(chargeSumToday));
                }
            }
            // 次均充电时长
            if(resultJson.containsKey("averageChargeTimeToday")){
                JSONArray averageChargeTimeInfo=(JSONArray) resultJson.get("averageChargeTimeToday");
                String averageChargeTimeToday=TimeseriesService.dealChargeTime((String) averageChargeTimeInfo.getJSONObject(0).get("value"));
                if(averageChargeTimeToday!=null){
                    usageInfo.setAverageChargeTimeYesterday(Double.valueOf(averageChargeTimeToday));
                }
            }
            // 充行时长比
            if(resultJson.containsKey("chargeTravelRatioToday")){
                JSONArray chargeTravelRatioInfo=(JSONArray) resultJson.get("chargeTravelRatioToday");
                // 待定
                String averageChargeTimeToday=TimeseriesService.dealchargeTravelRatio((String) chargeTravelRatioInfo.getJSONObject(0).get("value"));
                if(averageChargeTimeToday!=null){
                    usageInfo.setChargeTravelRatioYesterday(Double.valueOf(averageChargeTimeToday));
                }
            }

        }

        // 查询今日数据
        String paramsToday="?keys="+keys+"&startTs="+todayTs[0]+"&endTs="+todayTs[1];
        Result rsToday=timeseriesService.getTimeseriesHis(uuid,token,paramsToday);
        if(!rsToday.getSuccess()){
            return new Result(FAIL.code,false,null,rsToday.getMsg());
        }
        else {
            Map<String,String> resultData=new HashMap<>();// 保存计算结果
            JSONObject resultJson = (JSONObject) rsToday.getData();
            // 今日行驶里程
//            if (resultJson.containsKey("distance")){
//                resultData.putAll(decodeDistance(resultJson));  // 累计里程DISTANCE_SUM
//                usageInfo.setDistanceToday(Double.valueOf(resultData.get(DISTANCE_SUM)));
//                usageInfo.setDistance(Double.valueOf(resultData.get(DISTANCE_END)));
//            }
            if (resultJson.containsKey("distanceToday")){
                JSONArray distanceInfo = (JSONArray) resultJson.get("distanceToday");
                String distanceToday=TimeseriesService.dealDistance((String) distanceInfo.getJSONObject(0).get("value"));
                if(distanceToday!=null){
                    usageInfo.setDistanceToday(Double.valueOf(distanceToday));
                }
            }
            // 今日行驶时长
//            if (resultJson.containsKey("travelTime")){
//                resultData.putAll(decodeTravelTime(resultJson));  // 累计里程DISTANCE_SUM
//                usageInfo.setTravelTimeToday(Double.valueOf(resultData.get(TRAVEL_TIME_SUM)));
//                usageInfo.setTravelTime(Double.valueOf(resultData.get(TRAVEL_TIME_END)));
//            }
            if (resultJson.containsKey("travelTimeToday")){
                JSONArray travelTimeInfo = (JSONArray) resultJson.get("travelTimeToday");
                String travelTime=TimeseriesService.dealDischargeTime((String) travelTimeInfo.getJSONObject(0).get("value"));
                if(travelTime!=null){
                    usageInfo.setTravelTimeToday(Double.valueOf(travelTime));
                }
            }
            // 今日充电次数
            if (resultJson.containsKey("chargeNumToday")){
                JSONArray chargeNumInfo = (JSONArray) resultJson.get("chargeNumToday");
                String chargeNum=TimeseriesService.dealChargeNum((String) chargeNumInfo.getJSONObject(0).get("value"));
                if(chargeNum!=null){
                    usageInfo.setChargeNumToday(Long.parseLong(chargeNum));
                }
            }
            // 今日充电时长
            if (resultJson.containsKey("chargeTimeToday")){
                System.out.println("chargeTimeToday");
                JSONArray chargeTimeInfo = (JSONArray) resultJson.get("chargeTimeToday");
                String chargeTime=TimeseriesService.dealChargeTime((String) chargeTimeInfo.getJSONObject(0).get("value"));
                if(chargeTime!=null){
                    usageInfo.setChargeTimeToday(Double.valueOf(chargeTime));
                }
            }

            // 今日充电电量
//            if(resultJson.containsKey("electricChargeSum")){
//                resultData.putAll(decodeElectricChargeSum(resultJson));
//                usageInfo.setChargeSumToday(Double.valueOf(resultData.get(ELECTRIC_CHARGE_SUM)));
//                usageInfo.setChargeSum(Double.valueOf(resultData.get(ELECTRIC_CHARGE_END)));
//            }
            if(resultJson.containsKey("chargeSumToday")){
                JSONArray chargeInfo=(JSONArray) resultJson.get("chargeSumToday");
                String chargeSumToday=TimeseriesService.dealElectricSum((String) chargeInfo.getJSONObject(0).get("value"));
                if(chargeSumToday!=null){
                    usageInfo.setChargeSumToday(Double.valueOf(chargeSumToday));
                }
            }
            // 今日次均充电时长
            if(resultJson.containsKey("averageChargeTimeToday")){
                JSONArray averageChargeTimeInfo=(JSONArray) resultJson.get("averageChargeTimeToday");
                String averageChargeTimeToday=TimeseriesService.dealChargeTime((String) averageChargeTimeInfo.getJSONObject(0).get("value"));
                if(averageChargeTimeToday!=null){
                    usageInfo.setAverageChargeTimeToday(Double.valueOf(averageChargeTimeToday));
                }
            }
            // 今日充行时长比
            if(resultJson.containsKey("chargeTravelRatioToday")){
                JSONArray chargeTravelRatioInfo=(JSONArray) resultJson.get("chargeTravelRatioToday");
                // 待定
                String averageChargeTimeToday=TimeseriesService.dealchargeTravelRatio((String) chargeTravelRatioInfo.getJSONObject(0).get("value"));
                if(averageChargeTimeToday!=null){
                    usageInfo.setChargeTravelRatioToday(Double.valueOf(averageChargeTimeToday));
                }
            }
            // 能查到充电状态变化相关信息----------------------------------------------------------------累计的没有
//            if(resultJson.containsKey("chargeFlag")){
//                Map<String,Object> map=getChargeTimeScope(resultJson);
//                List<long[]> chargeTimeScope=(List<long[]>)map.get(CHARGE_TIME_SCOPE);
//                if(chargeTimeScope==null||chargeTimeScope.size()==0){
//                    // 充电次数，累计无法计算
//                    usageInfo.setChargeNumToday(0);
//                    // 充电时长，累计无法计算
//                    usageInfo.setChargeTimeToday(0.0);
//                    // 次均充电时长，累计无法计算
//                    usageInfo.setAverageChargeTimeToday(0.0);
//                    // 充行时长比，累计无法计算
//                    usageInfo.setChargeTravelRatioToday(0.0);
//                } else {
//                    // 充电次数
//                    usageInfo.setChargeNumToday(chargeTimeScope.size());
//                    // 充电时长
//                    usageInfo.setChargeTimeToday(getTotalTime(chargeTimeScope,(long) map.get(CHARGE_START_TS),(long) map.get(CHARGE_END_TS)));
//                    // 次均充电时长
//                    double chargeDuration=getTotalTime(chargeTimeScope,(long) map.get(CHARGE_START_TS),(long) map.get(CHARGE_END_TS))/chargeTimeScope.size();
//                    usageInfo.setAverageChargeTimeToday(Double.parseDouble(String.format("%.2f",chargeDuration)));// 保留两位小数
//                    // 充行时长比
//                    if(resultData.get(TRAVEL_TIME_SUM)==null||Double.parseDouble(resultData.get(TRAVEL_TIME_SUM))==0){
//                        usageInfo.setChargeTravelRatioToday(0.0);
//                    } else {
//                        double chargeTravelRatio=chargeDuration/Double.parseDouble(resultData.get(TRAVEL_TIME_SUM));
//                        usageInfo.setChargeTravelRatioToday(Double.parseDouble(String.format("%.2f",chargeTravelRatio)));
//                    }
//                }
//            }


        }
        // 计算累积数据 以及 日均数据
        keys="distanceTotal,travelTimeTotal,chargeNumTotal,chargeTimeTotal,chargeSumTotal,averageChargeTimeTotal,chargeTravelRatioTotal";
        String paramsTotal="?keys="+keys+"&startTs="+todayTs[0]+"&endTs="+todayTs[1];
        Result rsTotal=timeseriesService.getTimeseriesHis(uuid,token,paramsTotal);
        if(!rsTotal.getSuccess()){
            return new Result(FAIL.code,false,null,rsTotal.getMsg());
        }
        else {
//            Map<String, String> resultData = new HashMap<>();// 保存计算结果
            JSONObject resultJson = (JSONObject) rsTotal.getData();
            // 累计行驶里程
            if (resultJson.containsKey("distanceTotal")) {
                JSONArray distanceInfo = (JSONArray) resultJson.get("distanceTotal");
                String distanceTotal = TimeseriesService.dealDistance((String) distanceInfo.getJSONObject(0).get("value"));
                if (distanceTotal != null) {
                    usageInfo.setDistanceTotal(Double.valueOf(distanceTotal));
                    usageInfo.setDistanceDaily(Double.parseDouble(String.format("%2f",Double.parseDouble(distanceTotal)/totalDays)));
                }
            }
            // 累计行驶时长
            if (resultJson.containsKey("travelTimeTotal")) {
                JSONArray travelTimeInfo = (JSONArray) resultJson.get("travelTimeTotal");
                String travelTime = TimeseriesService.dealDischargeTime(travelTimeInfo.getJSONObject(0).getString("value"));
                if (travelTime != null) {
                    usageInfo.setTravelTimeTotal(Double.valueOf(travelTime));
                    usageInfo.setTravelTimeDaily(Double.parseDouble(String.format("%.2f",Double.parseDouble(travelTime)/totalDays)));
                }
            }
            // 累计充电次数
            if (resultJson.containsKey("chargeNumTotal")) {
                JSONArray chargeNumInfo = (JSONArray) resultJson.get("chargeNumTotal");
                String chargeNum = TimeseriesService.dealChargeNum((String) chargeNumInfo.getJSONObject(0).get("value"));
                if (chargeNum != null) {
                    usageInfo.setChargeNumTotal(Long.parseLong(chargeNum));
                    usageInfo.setChargeNumDaily(Double.parseDouble(String.format("%2f",Double.parseDouble(chargeNum)/totalDays)));
                }
            }
            // 累计充电时长
            if (resultJson.containsKey("chargeTimeTotal")) {
                JSONArray chargeTimeInfo = (JSONArray) resultJson.get("chargeTimeTotal");
                String chargeTime = TimeseriesService.dealChargeTime((String) chargeTimeInfo.getJSONObject(0).get("value"));
                if (chargeTime != null) {
                    usageInfo.setChargeTimeTotal(Double.valueOf(chargeTime));
                    usageInfo.setChargeTimeDaily(Double.parseDouble(String.format("%2f",Double.parseDouble(chargeTime)/totalDays)));
                }
            }
            // 累计充电电量
            if(resultJson.containsKey("chargeSumTotal")){
                JSONArray chargeInfo=(JSONArray) resultJson.get("chargeSumTotal");
                String chargeSum=TimeseriesService.dealElectricSum((String) chargeInfo.getJSONObject(0).get("value"));
                if(chargeSum!=null){
                    usageInfo.setChargeSumTotal(Double.valueOf(chargeSum));
                    usageInfo.setChargeSumDaily(Double.parseDouble(String.format("%2f",Double.parseDouble(chargeSum)/totalDays)));
                }
            }
            // 累计次均充电时长
            if(resultJson.containsKey("averageChargeTimeTotal")){
                JSONArray averageChargeTimeInfo=(JSONArray) resultJson.get("averageChargeTimeTotal");
                String averageChargeTime=TimeseriesService.dealChargeTime((String) averageChargeTimeInfo.getJSONObject(0).get("value"));
                if(averageChargeTime!=null){
                    usageInfo.setAverageChargeTimeTotal(Double.valueOf(averageChargeTime));
                    usageInfo.setAverageChargeTimeDaily(Double.parseDouble(String.format("%.2f",Double.parseDouble(averageChargeTime)/totalDays)));
                }
            }
            // 累计充行时长比
            if(resultJson.containsKey("chargeTravelRatioTotal")){
                JSONArray chargeTravelRatioInfo=(JSONArray) resultJson.get("chargeTravelRatioTotal");
                // 待定
                String averageChargeTime=TimeseriesService.dealchargeTravelRatio((String) chargeTravelRatioInfo.getJSONObject(0).get("value"));
                if(averageChargeTime!=null){
                    usageInfo.setChargeTravelRatioTotal(Double.valueOf(averageChargeTime));
                    usageInfo.setChargeTravelRatioDaily(Double.parseDouble(String.format("%2f",Double.parseDouble(averageChargeTime)/totalDays)));
                }
            }
        }


        return new Result(SUCCESS.code,true,usageInfo,"success");
    }



    /**
     * extra 2 充电记录接口
     */
    public Result getChargeRecords(String imei,Date startTime, Date endTime,String token) {
        List<ChargeRecord> list=new LinkedList<>();
        // 获取uuid
        String uuid=deviceService.getUUIDByImei(imei);
        String keys="chargeFlag";
        System.out.println("GetChargeRecord... startTs:"+startTime.getTime()+" endTs:"+endTime.getTime());
        String params="?keys="+keys+"&startTs="+startTime.getTime()+"&endTs="+endTime.getTime()+"&limit="+LIMIT;
        Result rs=timeseriesService.getTimeseriesHis(uuid,token,params);
        // 1. 根据flag获取每一段充电
        if(!rs.getSuccess()){
            return new Result(FAIL.code,false,null,rs.getMsg());
        }

        JSONObject resultJson = (JSONObject) rs.getData();
        if(!resultJson.containsKey("chargeFlag")){
            return new Result(FAIL.code,false,null,"No charge Status Change!");
        }

        Map<String, Object> map = getChargeTimeScope(resultJson);  // 获取充电范围
//        Map<String, Object> map = getDischargeTimeScope(resultJson); // 使用放电的进行测试
        // 2. 获取每一段充电的开始结束时间
        List<long[]> chargeTimeScope = (List<long[]>) map.get(CHARGE_TIME_SCOPE);
        if(chargeTimeScope == null || chargeTimeScope.size() == 0){// 没有进行充电
            return new Result(SUCCESS.code,true,list,"success");
        }
        log.info("chargeTimeScope:");
        for(long[] scope:chargeTimeScope){
            log.info("charge startTs:"+scope[0]+" endTs:"+scope[1]);
        }

        keys="chargeStationNo,rackTotalVoltage,electricChargeSum"; // 需要查询的参数:电压、电量、充电桩编号
        // 3. 遍历每一段充电，根据不同情况选择不同的查询方式
        for(long[] scope:chargeTimeScope){
            ChargeRecord chargeRecord=new ChargeRecord();
            // 充电时间和充电时长，需要分前后都被截断、前面被截断、后面被截断、完整的四种情况讨论
            if(scope[1]==Long.MAX_VALUE){ // 后面被截断的情况
                // 充电时间 chargeMoment，未结束提供开始时间
                log.info("后面被截断");
//                chargeRecord.setChargeMoment(DateUtil.timestampToString(scope[0]));
                // 1.前面后面都被截断
                if(scope[0]==Long.MIN_VALUE){ // --------可能缺失开始充电标志时，此情况同情况3，不会出现
                    log.info("前面也被截断");
                    // 充电时长
                    double chargeDuration=getMinuteDiff((long)map.get(CHARGE_START_TS),(long)map.get(CHARGE_END_TS));
                    chargeRecord.setChargeTime(Double.parseDouble(String.format("%.2f",chargeDuration)));
                    // 开始结束
                    chargeRecord.setStartTime(null);
                    chargeRecord.setEndTime(null);
                    // 查询其他
                    params="?keys="+keys+"&startTs="+map.get(CHARGE_START_TS)+"&endTs="+map.get(CHARGE_END_TS);
                } else { // 2.只有后面被截断
                    // 充电时长
                    double chargeDuration=getMinuteDiff(scope[0],(long)map.get(CHARGE_END_TS));
                    chargeRecord.setChargeTime(Double.parseDouble(String.format("%.2f",chargeDuration)));
                    // 开始结束时间
                    chargeRecord.setStartTime(DateUtil.timestampToString(scope[0]));
                    chargeRecord.setEndTime(null);
                    // 查询其他
                    params="?keys="+keys+"&startTs="+scope[0]+"&endTs="+map.get(CHARGE_END_TS);
                }

            }
            else if(scope[0]==Long.MIN_VALUE) { // 3.只有前面被截断-------------改完后这种情况不会发生，因为前面被截断时间也是用的startTs
                log.info("前面被截断");
                // 充电时间 chargeMoment，已结束提供结束时间
//                chargeRecord.setChargeMoment(DateUtil.timestampToString(scope[1]));
                chargeRecord.setStartTime(null);
                chargeRecord.setEndTime(DateUtil.timestampToString(scope[1]));
                // 查询其他
                params="?keys="+keys+"&startTs="+map.get(CHARGE_START_TS)+"&endTs="+scope[1];
            }
            else { // 完整的一段充电
                log.info("完整的一段充电");
                // 充电时长
                double chargeDuration=getMinuteDiff(scope[0],scope[1]);
                chargeRecord.setChargeTime(Double.parseDouble(String.format("%.2f",chargeDuration)));
                // 充电时间 chargeMoment，已结束提供结束时间
//                chargeRecord.setChargeMoment(DateUtil.timestampToString(scope[1]));
                chargeRecord.setStartTime(DateUtil.timestampToString(scope[0]));
                chargeRecord.setEndTime(DateUtil.timestampToString(scope[1]));
                // 查询其他所需要的参数
                params="?keys="+keys+"&startTs="+scope[0]+"&endTs="+scope[1]; // 完整的一段充电
            }

            // 二次修改完后只有这一种情况↓
//            chargeRecord.setStartTime(DateUtil.timestampToString(scope[0]));
//            chargeRecord.setEndTime(DateUtil.timestampToString(scope[1]));
//            params="?keys="+keys+"&startTs="+scope[0]+"&endTs="+scope[1];
            // 二次修改完后只有这一种情况↑


            // 查询其他字段
            params=params+"&limit="+LIMIT;
            Result rsRest=timeseriesService.getTimeseriesHis(uuid,token,params);
            if(!rsRest.getSuccess()){
                list.add(chargeRecord);
                continue;
            }
            JSONObject resultJsonRest = (JSONObject) rsRest.getData();
            Map<String,String> resultData=new HashMap<>();
            // 充电桩编号
            if(resultJsonRest.containsKey("chargeStationNo")){
                chargeRecord.setChargeStationNo(TimeseriesService.dealChargeStationNo(resultJsonRest.getJSONArray("chargeStationNo").getJSONObject(0).get("value").toString()));
            }
            // 开始、结束总电压
            if(resultJsonRest.containsKey("rackTotalVoltage")){
                resultData.putAll(decodeRackTotalVoltage(resultJsonRest));
                chargeRecord.setRackTotalVoltageStart(Double.valueOf(resultData.get(TOTAL_VOLTAGE_START)));
                chargeRecord.setRackTotalVoltageEnd(Double.valueOf(resultData.get(TOTAL_VOLTAGE_END)));
            }
            // 累计充电电量
            if(resultJsonRest.containsKey("electricChargeSum")){
                System.out.println("electricChargeSum");
                resultData.putAll(decodeElectricChargeSum(resultJsonRest));
                chargeRecord.setChargeSum(Double.valueOf(resultData.get(ELECTRIC_CHARGE_SUM)));
            }

            // 添加到list
            list.add(chargeRecord);
        }

        return new Result(SUCCESS.code,true,list,"success");
    }
    /**
     * 获取每一段充电状态的ts范围
     * @param info 完整的查询JSONObject数据，包括各种字段
     * @return 每一段充电的startTs和endTs，若开始就已在充电状态则startTs=MIN，若结束还未结束充电则entTs=MAX，此时获取START_TS和END_TS计算
     */
    private Map<String,Object> getChargeTimeScope(JSONObject info){
        Map<String,Object> map=new HashMap<>();
        List<long[]> chargeTimeList = new ArrayList<>();
        JSONArray chargeFlagArray = info.getJSONArray("chargeFlag");
        int size = chargeFlagArray.size();
        // 最开始的时间戳，便于一开始就已经是充电时计算截断的时间   注意！数组顺序为0-size为从新到旧，所以0为最新数据，即endTs
        long chargeStartTs=chargeFlagArray.getJSONObject(chargeFlagArray.size()-1).getLong("ts");
        long chargeEndTs=chargeFlagArray.getJSONObject(0).getLong("ts");
        boolean chargingFlag=false;// 标识是否在充电中
        long startTime=0;
        // 0初始 1开始充电 2充电结束 3开始放电 4放电结束 5充电中 6放电中
        // 注意！从startTs到endTs遍历，是从array[size-1]到array[0]遍历(数组顺序从后往前，时间顺序从前往后)
        for(int i=size-1;i>=0;i--){
            JSONObject obj = chargeFlagArray.getJSONObject(i);
            String value = TimeseriesService.dealChargeFlag(obj.getString("value"));
            if(value==null || value.equals(INVALID_DATA)){
                continue;
            }
            long ts = obj.getLong("ts");
            // 严格按照协议开始充电-充电中-结束充电
//            if(value.startsWith("5")){ // 充电中
//                if(!chargingFlag){ // 原来未在充电，说明前半部分被截断
//                    log.info("start charging time:null");
//                    startTime=Long.MIN_VALUE; //获取开始时间
//                    chargingFlag=true; //状态改为正在充电
//                }
//            } else if(value.startsWith("1")){ // 开始充电
//                if(!chargingFlag){ // 原来未在充电，说明刚开始充电
//                    log.info("start charging time:"+ts+" value:"+value);
//                    startTime=ts; //获取开始时间
//                    chargingFlag=true; //状态改为正在充电
//                }
//            } else if(chargingFlag&&(value.startsWith("2")||value.startsWith("0"))){ // 结束充电标志或者直接回到了初始状态
//                log.info("end charging time:"+ts+" value:"+value);
//                long endTime=ts; // 获取结束时间
//                chargeTimeList.add(new long[]{startTime,endTime});// 添加到一段充电
//                chargingFlag=false; //状态改为非充电
//            }

            // 可能缺失开始充电或结束充电标志
            if(value.startsWith("5")||value.startsWith("1")){//  5(充电中)是用来判断一开始已经在充电状态(前面被截断)， 1(开始充电)是未被截断
//                System.out.println("startWith:"+value+" time:"+ts);
                if(!chargingFlag){ // 原先未在充电
                    log.info("start discharging time:"+ts+" value:"+value);
                    startTime=ts; //获取开始时间
                    chargingFlag=true; //状态改为正在充电
                }
            } else if(chargingFlag&&(value.startsWith("2")||value.startsWith("0"))){ // 结束充电标志或者直接回到了初始状态
                log.info("end charging time:"+ts+" value:"+value);
                long endTime=ts; // 获取结束时间
                chargeTimeList.add(new long[]{startTime,endTime});// 添加到一段充电
                chargingFlag=false; //状态改为非充电
            }
        }
        if(chargingFlag){// 最后一段充电还未结束
            log.info("充电至endTs还未结束");
            chargeTimeList.add(new long[]{startTime,Long.MAX_VALUE});
//            chargeTimeList.add(new long[]{startTime,chargeEndTs}); // --------------------------修改为直接传入最后的时间戳
        }
        map.put(CHARGE_TIME_SCOPE, chargeTimeList); // 每一段的放电开始和结束时间，若开始被前面截断则startTs=MIN，若结束被后面截断则endTs=MAX
        map.put(CHARGE_START_TS, chargeStartTs); // 第一项的ts
        map.put(CHARGE_END_TS, chargeEndTs); // 最后一项的ts
        return map;
    }
    private Map<String,Object> getDischargeTimeScope(JSONObject info){// 获取放电范围，测试用-------------要正式用的话记得Map的key改了别和充电重了
        Map<String,Object> map=new HashMap<>();
        List<long[]> chargeTimeList = new ArrayList<>();
        JSONArray chargeFlagArray = info.getJSONArray("chargeFlag");
        int size = chargeFlagArray.size();
        // 最开始的时间戳，便于一开始就已经是充电时计算截断的时间   注意！数组顺序为0-size为从新到旧，所以0为最新数据，即endTs
        long chargeStartTs=chargeFlagArray.getJSONObject(chargeFlagArray.size()-1).getLong("ts");
        long chargeEndTs=chargeFlagArray.getJSONObject(0).getLong("ts");
        boolean chargingFlag=false;// 标识是否在充电中
        long startTime=0;
        // 0初始 1开始充电 2充电结束 3开始放电 4放电结束 5充电中 6放电中
        // 注意！从startTs到endTs遍历，是从array[size-1]到array[0]遍历(数组顺序从后往前，时间顺序从前往后)
        for(int i=size-1;i>=0;i--){
            JSONObject obj = chargeFlagArray.getJSONObject(i);
            String value = TimeseriesService.dealChargeFlag(obj.getString("value"));
            if(value==null){
                continue;
            }
            long ts = obj.getLong("ts");

            if(value.startsWith("6")||value.startsWith("3")){//  6(放电中)是用来判断一开始已经在充电状态(前面被截断)， 3(开始放电)是未被截断
//                System.out.println("startWith:"+value+" time:"+ts);
                if(!chargingFlag){ // 原先未在放电
                    System.out.println("start discharging time:"+ts+" value:"+value);
                    startTime=ts; //获取开始时间
                    chargingFlag=true; //状态改为正在放电
                }
            } else if(chargingFlag&&(value.startsWith("4")||value.startsWith("0"))){ // 结束放电标志
                System.out.println("end discharging time:"+ts+" value:"+value);
                long endTime=ts; // 获取结束时间
                chargeTimeList.add(new long[]{startTime,endTime});// 添加到一段充电
                chargingFlag=false; //状态改为非充电
            }
        }
        if(chargingFlag){// 最后一段充电还未结束
            System.out.println("放电至endTs还未结束");
            chargeTimeList.add(new long[]{startTime,Long.MAX_VALUE});
        }
        map.put(CHARGE_TIME_SCOPE, chargeTimeList); // 每一段的放电开始和结束时间，若开始被前面截断则startTs=MIN，若结束被后面截断则endTs=MAX
        map.put(CHARGE_START_TS, chargeStartTs); // 第一项的ts
        map.put(CHARGE_END_TS, chargeEndTs); // 最后一项的ts
        return map;
    }

    /**
     * extra 3 电池历史信息接口
     */
    public Result getBatteryHisInfo(String imei,Date startTime, Date endTime,String token){
        BatteryHisInfo batteryHisInfo=new BatteryHisInfo();
        // 获取uuid
        String uuid=deviceService.getUUIDByImei(imei);
        // packNo
        batteryHisInfo.setPackNo(timeseriesService.getPackSN(uuid,token).get("PACK_SN"));
        // imei
        batteryHisInfo.setImei(imei);
        String keys="rackTotalVoltage,rackTotalCurrent,maxCellVoltage,maxCellVoltageCellid,"+
                    "minCellVoltage,minCellVoltageCellid,racketSOC,racketSOH,"+
                    "maxTemp,minTemp,electricDischargeSum,electricChargeSum";
        String params="?keys="+keys+"&startTs="+startTime.getTime()+"&endTs="+endTime.getTime()+"&limit="+LIMIT;
        Result rs=timeseriesService.getTimeseriesHis(uuid,token,params);
        if(!rs.getSuccess()){
            return new Result(FAIL.code,false,null,rs.getMsg());
        }

        JSONObject resultJson = (JSONObject) rs.getData();
        // 当前总电压
        if(resultJson.containsKey("rackTotalVoltage")){
            JSONArray array=resultJson.getJSONArray("rackTotalVoltage");
            // 遍历添加
            for(int i=0;i<array.size();i++){
                TimeseriesData timeseriesData=new TimeseriesData();
                timeseriesData.setCollectTime(DateUtil.timestampToString(Long.parseLong(array.getJSONObject(i).get("ts").toString())));// 时间
                timeseriesData.setValue(Double.parseDouble(TimeseriesService.dealRackTotalVoltage(array.getJSONObject(i).get("value").toString()))); // 值
                batteryHisInfo.addToRackTotalVoltage(timeseriesData);
            }
        }
        //当前总电流
        if(resultJson.containsKey("rackTotalCurrent")){
            JSONArray array=resultJson.getJSONArray("rackTotalCurrent");
            // 遍历添加
            for(int i=0;i<array.size();i++){
                TimeseriesData timeseriesData=new TimeseriesData();
                timeseriesData.setCollectTime(DateUtil.timestampToString(Long.parseLong(array.getJSONObject(i).get("ts").toString())));// 时间
                timeseriesData.setValue(Double.parseDouble(TimeseriesService.dealRackTotalCurrent(array.getJSONObject(i).get("value").toString()))); // 值
                batteryHisInfo.addToRackTotalCurrent(timeseriesData);
            }
        }
        // 单体最高电压
        if(resultJson.containsKey("maxCellVoltage")){
            JSONArray array=resultJson.getJSONArray("maxCellVoltage");
            // 遍历添加
            for(int i=0;i<array.size();i++){
                TimeseriesData timeseriesData=new TimeseriesData();
                timeseriesData.setCollectTime(DateUtil.timestampToString(Long.parseLong(array.getJSONObject(i).get("ts").toString())));// 时间
                timeseriesData.setValue(Integer.parseInt(TimeseriesService.dealMaxCellVoltage(array.getJSONObject(i).get("value").toString()))); // 值
                batteryHisInfo.addToRackMaxCellVoltage(timeseriesData);
            }
        }
        // 单体最高电压对应序号
        if(resultJson.containsKey("maxCellVoltageCellid")){
            JSONArray array=resultJson.getJSONArray("maxCellVoltageCellid");
            // 遍历添加
            for(int i=0;i<array.size();i++){
                TimeseriesData timeseriesData=new TimeseriesData();
                timeseriesData.setCollectTime(DateUtil.timestampToString(Long.parseLong(array.getJSONObject(i).get("ts").toString())));// 时间
                timeseriesData.setValue(Integer.parseInt(TimeseriesService.dealMaxCellVoltageCellid(array.getJSONObject(i).get("value").toString()))); // 值
                batteryHisInfo.addToRackMaxVolCellID(timeseriesData);
            }
        }
        // 单体最低电压
        if(resultJson.containsKey("minCellVoltage")){
            JSONArray array=resultJson.getJSONArray("minCellVoltage");
            // 遍历添加
            for(int i=0;i<array.size();i++){
                TimeseriesData timeseriesData=new TimeseriesData();
                timeseriesData.setCollectTime(DateUtil.timestampToString(Long.parseLong(array.getJSONObject(i).get("ts").toString())));// 时间
                timeseriesData.setValue(Integer.parseInt(TimeseriesService.dealMinCellVoltage(array.getJSONObject(i).get("value").toString()))); // 值
                batteryHisInfo.addToRackMinCellVoltage(timeseriesData);
            }
        }
        // 单体最低电压对应序号
        if(resultJson.containsKey("minCellVoltageCellid")){
            JSONArray array=resultJson.getJSONArray("minCellVoltageCellid");
            // 遍历添加
            for(int i=0;i<array.size();i++){
                TimeseriesData timeseriesData=new TimeseriesData();
                timeseriesData.setCollectTime(DateUtil.timestampToString(Long.parseLong(array.getJSONObject(i).get("ts").toString())));// 时间
                timeseriesData.setValue(Integer.parseInt(TimeseriesService.dealMinCellVoltageCellid(array.getJSONObject(i).get("value").toString()))); // 值
                batteryHisInfo.addToRackMinVolCellID(timeseriesData);
            }
        }
        // 当前电量SOC
        if(resultJson.containsKey("racketSOC")){
            JSONArray array=resultJson.getJSONArray("racketSOC");
            // 遍历添加
            for(int i=0;i<array.size();i++){
                TimeseriesData timeseriesData=new TimeseriesData();
                timeseriesData.setCollectTime(DateUtil.timestampToString(Long.parseLong(array.getJSONObject(i).get("ts").toString())));// 时间
                timeseriesData.setValue(Double.parseDouble(TimeseriesService.dealRacketSOCAndSOH(array.getJSONObject(i).get("value").toString()))); // 值
                batteryHisInfo.addToRackSOC(timeseriesData);
            }
        }
        // 电池健康值SOH
        if(resultJson.containsKey("racketSOH")){
            JSONArray array=resultJson.getJSONArray("racketSOH");
            // 遍历添加
            for(int i=0;i<array.size();i++){
                TimeseriesData timeseriesData=new TimeseriesData();
                timeseriesData.setCollectTime(DateUtil.timestampToString(Long.parseLong(array.getJSONObject(i).get("ts").toString())));// 时间
                timeseriesData.setValue(Double.parseDouble(TimeseriesService.dealRacketSOCAndSOH(array.getJSONObject(i).get("value").toString()))); // 值
                batteryHisInfo.addToRackSOH(timeseriesData);
            }
        }
        // 电芯最高温度
        if(resultJson.containsKey("maxTemp")){
            JSONArray array=resultJson.getJSONArray("maxTemp");
            // 遍历添加
            for(int i=0;i<array.size();i++){
                TimeseriesData timeseriesData=new TimeseriesData();
                timeseriesData.setCollectTime(DateUtil.timestampToString(Long.parseLong(array.getJSONObject(i).get("ts").toString())));// 时间
                timeseriesData.setValue(Double.parseDouble(TimeseriesService.dealMaxTemp(array.getJSONObject(i).get("value").toString()))); // 值
                batteryHisInfo.addToCellHightTemp(timeseriesData);
            }
        }
        // 电芯最低温度
        if(resultJson.containsKey("minTemp")){
            JSONArray array=resultJson.getJSONArray("minTemp");
            // 遍历添加
            for(int i=0;i<array.size();i++){
                TimeseriesData timeseriesData=new TimeseriesData();
                timeseriesData.setCollectTime(DateUtil.timestampToString(Long.parseLong(array.getJSONObject(i).get("ts").toString())));// 时间
                timeseriesData.setValue(Double.parseDouble(TimeseriesService.dealMinTemp(array.getJSONObject(i).get("value").toString()))); // 值
                batteryHisInfo.addToCellLowTemp(timeseriesData);
            }
        }
        // 累计放电容量
        if(resultJson.containsKey("electricDischargeSum")){
            JSONArray array=resultJson.getJSONArray("electricDischargeSum");
            // 遍历添加
            for(int i=0;i<array.size();i++){
                TimeseriesData timeseriesData=new TimeseriesData();
                timeseriesData.setCollectTime(DateUtil.timestampToString(Long.parseLong(array.getJSONObject(i).get("ts").toString())));// 时间
                timeseriesData.setValue(Double.parseDouble(TimeseriesService.dealElectricSum(array.getJSONObject(i).get("value").toString()))); // 值
                batteryHisInfo.addToElectricDischargeSum(timeseriesData);
            }
        }
        // 累计充电容量
        if(resultJson.containsKey("electricChargeSum")){
            JSONArray array=resultJson.getJSONArray("electricChargeSum");
            // 遍历添加
            for(int i=0;i<array.size();i++){
                TimeseriesData timeseriesData=new TimeseriesData();
                timeseriesData.setCollectTime(DateUtil.timestampToString(Long.parseLong(array.getJSONObject(i).get("ts").toString())));// 时间
                timeseriesData.setValue(Double.parseDouble(TimeseriesService.dealElectricSum(array.getJSONObject(i).get("value").toString()))); // 值
                batteryHisInfo.addToElectricChargeSum(timeseriesData);
            }
        }
        return new Result(SUCCESS.code,true,batteryHisInfo,"success");
    }

    /**
     * extra 4 业务状态接口
     */
    public Result setBusinessStatus(String imei,Integer businessStatus,String token){
        String uuid = deviceService.getUUIDByImei(imei);
        if(uuid==null){
            return new Result(FAIL.code, false, null,"Invalid UUID string: null");
        }
        // 构造消息------------------------------------------------------------------------------还没写呢
        if(businessStatus!=0&&businessStatus!=1&&businessStatus!=2){// 状态错误
            return new Result(FAIL.code, false, null,"Please input correct business status code");
        }
        String message= decimalToHex(0) +decimalToHex(businessStatus);
        System.out.println("business msg:"+message);
        try{
            //发送控制并获取结果
            MqttUtil mqttUtil=new MqttUtil();
            String topicPub=ConstantUtil.TOPIC_PREFIX + imei + ConstantUtil.PUB_TOPIC_SUFFIX_BUSINESS;
            String topicSub=ConstantUtil.TOPIC_PREFIX + imei + ConstantUtil.SUB_TOPIC_SUFFIX_BUSINESS;
            // 发送业务状态使用与控制接口相同的方法，但是不需要传入控制码
            String response=mqttUtil.sendControlMessage(apiConfig.getMqttBroker(),message,topicPub,topicSub,null);
            System.out.println("response:"+response);
            //没有获取到反馈信息
            if(response==null){
                //发送到mqtt启动轮询
                return new Result(FAIL.code,false,null, "send success,but response timed out");
            }
            // 判断是否设置成功--------------------------------------------------------------还没写呢
            if(message.equals(response)){
                return new Result(SUCCESS.code, true, true,"success");
            } else {
                return new Result(FAIL.code, false, null,"Business Status set failed");
            }

        } catch (Exception e) {
            log.error("oops!sth wrong!",e);
        }
        return new Result(FAIL.code, false, null,"Cannot set business status");
    }



    /**
     * 获取所有充（放）电的总时长
     * @param list 每一段的时间范围
     * @param startTs 前面被截断时用，即遇到startTs=MIN
     * @param endTs 后面被截断时用，即遇到endTs=MAX
     * @return 总时长（单位：分钟）
     */
    private Double getTotalTime(List<long[]> list,long startTs,long endTs){
        Double totalTime=0.0;
        for(long[] scope:list){
            if(scope[0]==Long.MIN_VALUE){ //前面被截断
                totalTime+=getMinuteDiff(startTs,scope[1]);

            }else if(scope[1]==Long.MAX_VALUE){
                totalTime+=getMinuteDiff(scope[0],endTs);
            }else {
                totalTime+=getMinuteDiff(scope[0],scope[1]);
            }
        }
        return totalTime;
    }
    private double getMinuteDiff(long startTime, long endTime) {// 获取两个时间戳之间的分钟差距
        LocalDateTime startDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneId.systemDefault());
        LocalDateTime endDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(endTime), ZoneId.systemDefault());
        Duration duration = Duration.between(startDateTime, endDateTime);
        long seconds = duration.getSeconds();
        double minutes = seconds / 60.0;
        return minutes;
    }


}


